EoPL reading (16) 1.2.4 Exercises

Exercise-1.18-2

これは正にテストファーストな問題に見えます。
とりあえず試験を作成。戻りが長いので微妙ですが以下。

(use gauche.test)

(add-load-path ".")
(load "carcdr")

(test-start "car&cdr")
(test-section "car&cdr")
(test* "(car&cdr 'a '(a b c) 'fail) should return car"
       'car
       (car&cdr 'a '(a b c) 'fail))
(test* "(car&cdr 'c '(a b c) 'fail) should return (compose car (compose cdr cdr))"
       '(compose car (compose cdr cdr))
       (car&cdr 'c '(a b c) 'fail))
(test* "(car&cdr 'doc '(cat lion (fish dog ()) pig)) 'fail) should return 
                 (compose car (compose cdr (compose car (compose cdr cdr))))"
       '(compose car (compose cdr (compose car (compose cdr cdr))))
       (car&cdr 'doc '(cat lion (fish dog ()) pig) 'fail))
(test* "(car&cdr 'a '(b c) 'fail) should return fail"
       'fail
       (car&cdr 'a '(b c) 'fail))
(test-end)

上から順にパスする形で実装を書いてみる。これ再起よりは繰り返しの方が書きやすいのかな。平たいリストで考えてみたら (car&cdr 'a '(a b c)) だと

  • car を取り出したらマッチ

という事で car が戻れば良い。あるいは (car&cdr 'b '(a b c)) だと

  • 取り出した car はマッチしないので cdr
  • 取り出した car がマッチ

という事で (compose car cdr) が戻れば良いのか。あるいは (car&cdr 'c '(a b c)) だと

  • 取り出した car はマッチしないので cdr
  • 取り出した car はマッチしないので cdr
  • 取り出した car がマッチ

という事で (compose car (compose cdr cdr)) が戻る、と。errvalue が戻るケイスは略。とりあえず適当に以下なカンジをでっちあげて試験を動かしてみた。

(define (car&cdr s slist errvalue)
  (define (car&cdr-inner rslt slist)
    (cond ((null? slist) errvalue)
	  ((eq? s (car slist))
	   (cond ((null? rslt) 'car)
		 (else (cons 'compose (cons 'car rslt)))))
	  (else
	   (car&cdr-inner
	    (cond ((null? rslt) 'cdr)
		  (else (cons 'compose (cons 'cdr rslt))))
	    (cdr slist))))
    )
  (car&cdr-inner '() slist)
  )

当たり前ですが試験にはパスしません。

$ cat test.log
Testing car&cdr ===============================================================
<car&cdr>----------------------------------------------------------------------
test (car&cdr 'a '(a b c) 'fail) should return car, expects car ==> ok
test (car&cdr 'c '(a b c) 'fail) should return (compose car (compose cdr cdr)), expects (compose car (compose cdr cdr)) ==> ERROR: GOT (compose car compose cdr . cdr)
test (car&cdr 'doc '(cat lion (fish dog ()) pig)) 'fail) should return 
                 (compose car (compose cdr (compose car (compose cdr cdr)))), expects (compose car (compose cdr (compose car (compose cdr cdr)))) ==> ERROR: GOT fail
test (car&cdr 'a '(b c) 'fail) should return fail, expects fail ==> ok
failed.
discrepancies found.  Errors are:
test (car&cdr 'c '(a b c) 'fail) should return (compose car (compose cdr cdr)): expects (compose car (compose cdr cdr)) => got (compose car compose cdr . cdr)
test (car&cdr 'doc '(cat lion (fish dog ()) pig)) 'fail) should return 
                 (compose car (compose cdr (compose car (compose cdr cdr)))): expects (compose car (compose cdr (compose car (compose cdr cdr)))) => got fail
$

横に長いので非常に見辛い。んですが、戻すリストの作り方が微妙。ちなみに平たくないリストの探索は現時点では考慮の範疇外です。とりあえず以下にしたら平たいリストの処理については上手く動作している模様。

(define (car&cdr s slist errvalue)
  (define (car&cdr-inner rslt slist)
    (cond ((null? slist) errvalue)
	  ((eq? s (car slist))
	   (cond ((null? rslt) 'car)
		 (else (list 'compose 'car rslt))))
	  (else
	   (car&cdr-inner
	    (cond ((null? rslt) 'cdr)
		  (else (list 'compose 'cdr rslt)))
	    (cdr slist))))
    )
  (car&cdr-inner '() slist)
  )

ええと、平たくないリストの場合ですが (car&cdr 'doc '(cat lion (fish dog ()) pig) 'fail) で考えたら

  • 取り出した car はマッチしないので cdr
  • 取り出した car は pair なので (ry

む。この場合 car&cdr な繰り返しに突入する前に取り出した car なリストをパースして見つからなければ云々?
ちょっともう少しきちんと考えてみる。

  • 取り出した car はマッチしないので cdr
  • 取り出した car はマッチしないので cdr
  • 取り出した car はリスト (car)
    • 取り出した car はマッチしないので cdr
    • 取り出した car はマッチ

で、どうもマッチしないって思ってたら試験が以下だった

(test* "(car&cdr 'doc '(cat lion (fish dog ()) pig)) 'fail) should return 
                 (compose car (compose cdr (compose car (compose cdr cdr))))"
       '(compose car (compose cdr (compose car (compose cdr cdr))))
       (car&cdr 'doc '(cat lion (fish dog ()) pig) 'fail))

何故に doc なんだ。そして試験をパスさせるためだけの手続きがでっち上がりました。

(define (car&cdr s slist errvalue)
  (define (car&cdr-inner rslt slist)
    (cond ((null? slist) errvalue)
	  ((eq? s (car slist))
	   (cond ((null? rslt) 'car)
		 (else (list 'compose 'car rslt))))
	  ((pair? (car slist))
	   (let ((ret (car&cdr-inner (list 'compose 'car rslt) (car slist))))
	     (if (eq? errvalue ret)
		 (car&cdr-inner
		  (cond ((null? rslt) 'cdr)
			(else (list 'compose 'cdr rslt)))
		  (cdr slist))
		 ret)))
	  (else
	   (car&cdr-inner
	    (cond ((null? rslt) 'cdr)
		  (else (list 'compose 'cdr rslt)))
	    (cdr slist))))
    )
  (car&cdr-inner '() slist)
  )

凄く紆余曲折があったし、異なるテストケイスで正常動作しない事が容易に予想できるんですが、恥知らずなわしはこのままコードをサラす。(キレ気味

明日

なんとかして対応稼動を確保予定。(を