EoPL reading (43) 2.1 Specifying Data via Interfaces
Exercise.2-1
うーん。bigits って何だろ、ググッても微妙なナニしか出てこなかったんですが Method for data compression という所にて以下な記述があった。
"BIGIT" A place holder in the binary number system. For example, a byte is a binary number having a length of 8 bigits. A bigit is analogous to a digit in the base 10 number system.
上記を参考に手続きをでっち上げれば良いのか。
しかしどうしたものやら。こんなカンジで表現すりゃ良い?
0 = '() 1 = '(1) 2 = '(0 1) 3 = '(1 1) 4 = '(0 0 1)
これって 2 値だから #t と #f なリストでも良いのか。てーコトは
(define zero '()) (define iszero? null?)
で OK か。あるいはこう?
(define zero '(#f)) (define iszero? (lambda (x) (and (= 1 (length x)) (eq? #f (car x)))))
ちょっと試験書いて実装するか。以下な試験を書きました。
(use gauche.test) (add-load-path ".") (load "bigits") (test-start "bigits") (test-section "bigits") (test* "zero should return '(#f)" '(#f) zero) (test* "(iszero? zero) shuould return #t" #t (iszero? zero)) (test* "(not (iszero? zero)) should return #f" #f (not (iszero? zero))) (test-end)
試験はパス。
Compilation started at Fri Jul 31 22:22:33 make -k Testing bigits ... passed. Compilation finished at Fri Jul 31 22:22:33
あとは succ と pred の試験と実装か。とりあえずこんな試験を追加。
(test-section "succ") (test* "(succ zero) should return '(#t)" '(#t) (succ zero))
ええと、
- car が #f なら (cons #t (cdr l)) で OK
- car が #t なら (cons #f (succ (cdr l))) で OK?
とりあえず #f 前提で試験だけ通そう。実装は以下。
(define succ (lambda (x) (cons #t (cdr x))))
(cdr l) ってヤッちゃったのは秘密。もう一つのソレも試験。
(test* "(succ (succ zero)) should return '(#f #t)" '(#f #t) (succ (succ zero)))
紆余曲折の末、以下の実装で上記の試験にパス。
(define succ (lambda (x) (cond ((null? x) '(#t)) ((eq? #f (car x)) (cons #t (cdr x))) (else (cons #f (succ (cdr x)))))))
もう一つヤッてみます。
Testing bigits ================================================================ <zero & iszero?>--------------------------------------------------------------- test zero should return '(#f), expects (#f) ==> ok test (iszero? zero) shuould return #t, expects #t ==> ok test (not (iszero? zero)) should return #f, expects #f ==> ok <succ>------------------------------------------------------------------------- test (succ zero) should return '(#t), expects (#t) ==> ok test (succ (succ zero)) should return '(#f #t), expects (#f #t) ==> ok test (succ (succ (succ zero))) should return '(#t #t), expects (#t #t) ==> ok passed.
一応試験パスな模様。次は pred ですか。テストデータは作れるので試験は楽ですが実装が微妙。とりあえず最初の試験が以下。
(test-section "pred") (test* "(pred (succ zero)) should return zero" zero (pred (succ zero)))
実装は負の値、というか zero が渡されない前提にて。上記試験をパスするだけの実装が以下。
(define pred (lambda (x) (cons #f (cdr x))))
一応パス。
Testing bigits ================================================================ <zero & iszero?>--------------------------------------------------------------- test zero should return '(#f), expects (#f) ==> ok test (iszero? zero) shuould return #t, expects #t ==> ok test (not (iszero? zero)) should return #f, expects #f ==> ok <succ>------------------------------------------------------------------------- test (succ zero) should return '(#t), expects (#t) ==> ok test (succ (succ zero)) should return '(#f #t), expects (#f #t) ==> ok test (succ (succ (succ zero))) should return '(#t #t), expects (#t #t) ==> ok test (succ (succ (succ (succ zero)))) should return '(#f #f #t), expects (#f #f #t) ==> ok <pred>------------------------------------------------------------------------- test (pred (succ zero)) should return zero, expects (#f) ==> ok passed.
で、以下の実装にしてみたら
(define pred (lambda (x) (cons ((eq? #t (car x)) (cons #f (cdr x))) (else (cons #t (pred (cdr x)))))))
失敗。
Compilation started at Fri Jul 31 23:02:44 make -k Testing bigits ... failed. discrepancies found. Errors are: test (pred (succ zero)) should return zero: expects (#f) => got #<error "invalid application: (#t (#f))"> Compilation finished at Fri Jul 31 23:02:44
げ。これは微妙だな。って cond を cons って書いてます。。修正したらパス。次に以下の試験を書いた。
(test* "(pred (succ (succ (succ (succ zero))))) should return '(#t #t)" '(#t #t) (pred (succ (succ (succ (succ zero))))))
これが試験にパスしない。
test (pred (succ (succ (succ (succ zero))))) should return '(#t #t): expects (#t #t) => got (#t #t #f)
むむ。どうすりゃ良いのか。ええと、アタマが '(#f) になったらツブせば良い?
で、試験したら以下。
Compilation started at Fri Jul 31 23:26:58 make -k Testing bigits ... failed. discrepancies found. Errors are: test (pred '(#t)) should return zero: expects (#f) => got () test (pred (succ zero)) should return zero: expects (#f) => got () Compilation finished at Fri Jul 31 23:26:58
むむ。これはこれは。仕方が無いんで zero と iszero? の定義を以下に変更。
(define zero '()) (define iszero? null?)
で、pred の試験をもう少し追加。
(test* "(pred (pred (succ (succ (succ (succ zero)))))) should return '(#f #t)" '(#f #t) (pred (pred (succ (succ (succ (succ zero))))))) (test* "(pred (pred (pred (succ (succ (succ (succ zero))))))) should return '(#t)" '(#t) (pred (pred (pred (succ (succ (succ (succ zero)))))))) (test* "(pred (pred (pred (pred (succ (succ (succ (succ zero))))))) should return zero" zero (pred (pred (pred (pred (succ (succ (succ (succ zero)))))))))
いずれもパス。で、次は 10 の階乗ッスか。あるいは例示されてる plus が正常に動くかどうかも確認した方が良さげ。
(test-section "plus") (test* "(plus (succ (succ zero)) (succ (succ (succ zero)))) should returns '(#t #f #t)" '(#t #f #t) (plus (succ (succ zero)) (succ (succ (succ zero)))))
一応試験にはパス。plus の実装は以下です。
(define plus (lambda (x y) (if (iszero? x) y (succ (plus (pred x) y)))))
では factorial なナニに着手。2 を掛けたり割ったりは得意なデータ構造なんだけどなぁ。ちょっと設問の意図も微妙に理解できてなかったりします。
日が変わりそうなので一旦投入。なんとなく階乗の答えは出そうにない気がします。
google 先生に聞いてみたら
2 進の乗算とか色々あるみたい。とりあえずココを参考にしつつ手続きをでっち上げてみます。
試験が以下。
(test-section "mul") (test* "(mul (succ (succ zero)) (succ (succ (succ zero)))) should return '(#f #t #t)" '(#f #t #t) (mul (succ (succ zero)) (succ (succ (succ zero)))))
カンニングしつつ作った実装が以下。
(define mul (lambda (x y) (let f ((R0 x) (Acc y) (rslt zero)) (cond ((null? Acc) rslt) ((car Acc) (f (cons 0 R0) (cdr Acc) (plus R0 rslt))) (else (f (cons 0 R0) (cdr Acc) rslt))) ) ))
試験にはパスしてます。本当に大丈夫なんだろうか。とりあえず factorial は定義できそげなカンジですが、遅いのでもう寝る。