EoPL reading (25) 1.3.1 Free and Bound Variables
Exercise-1.20
例えば y が occurs free な lambda 式は以下。
((lambda (x) x) y)
ただ上記の式の値は y の値に依存している。occurs free な変数の値に依存しない、となると、どんな式書けばええんかな。これはふざけすぎ?
((lambda (x) x) (if (symbol? y) 1 1))
y が何でも 1 が戻る。
Exercise-1.21
これはこんなカンジ?
((lambda (x) x) x)
これを occurs-free? とか occurs-bound? に吸わせたらどうなるんでしょ。まず occurs-free? だと #t が戻るはず。(lambda (x) x) と x のどっちかが occurs-free? だと、という判断基準なので。
test (occurs-free? 'x '((lambda (x) x) x)) should return #t, expects #t ==> ok
次、occurs-bound? は、というとこれも or なので #t ですな。
test (occurs-bound? 'x '((lambda (x) x) x)) should return #t, expects #t ==> ok
ビンゴ。ut なツールってこういった使い方ができるのが良いですよね。
Exercise-1.22
例示されている手続きは
- lambda 式の引数が一つ限定
- 手続きに渡す引数も一つ限定
なんですが、これを any number に、との事。とりあえず試験を検討。
(test-section "ex.1-22") (test* "(occurs-free? 'x '((lambda (x y) (+ x y)) 1 2)) should return #t" #t (occurs-free? 'x '((lambda (x y) (+ x y)) 1 2)))
を追加して試験実施。
$ make Testing occurs-bound ... passed. Testing occurs-free ... failed. discrepancies found. Errors are: test (occurs-free? 'x '((lambda (x y) (+ x y)) 1 2)) should return #t: expects #t => got #<error "pair required, but got 1"> $
あ、上記は symbol じゃなかったら list としてナニ、な仕様なのか。ここはスルーしよう。もしかしてこれってパスするんかな。
(test-section "ex.1-22") (test* "(occurs-free? 'x '((lambda (x y) (+ x y)) x z)) should return #t" #t (occurs-free? 'x '((lambda (x y) (+ x y)) x z)))
パスした。こうしたらどうなるか。
(occurs-free? 'y '((lambda (x y) (+ x y)) x z))
たぶん #f が戻る。あ、これは #f で良いのか。これは?
(occurs-free? 'y '((lambda (x y) (+ x y)) x y))
#f ですな。ちなみに以下だと
(occurs-free? 'x '((lambda (x y) (+ x y)) x y))
#t が戻るはず。挙動もそうなってます。そろそろ実装を、なんですが lambda の引数のナニをどうするか、が悩ましい。else の部分はぶっちゃけ
(else (or (occurs-free? var (car exp)) (occurs-free? var (cdr exp)))))))
いいような気がしてるんですが (直感)、lambda の引数は cadr なリストを全部検査する手続きが必要か。むりくりヤッツけてみると以下?
(define occurs-free? (lambda (var exp) (cond ((null? exp) #t) ((symbol? exp) (eqv? exp var)) ((eqv? (car exp) 'lambda) ;; (and (not (eqv? (caadr exp) var)) (and (not (let f ((l (cadr exp))) (cond ((null? exp) #f) ((eqv? (car l) var) #t) (else (f (cdr l)))))) ;; (occurs-free? var (caddr exp)))) (occurs-free? var (cddr exp)))) (else (or (occurs-free? var (car exp)) ;; (occurs-free? var (cadr exp))))))) (occurs-free? var (cdr exp)))))))
試験にパスせず。まず気がついたのが名前付き let の f な手続きの
(cond ((null? exp) #f)
何故に exp なんだ、という事で (null? l) に修正してもパスせず。もう一点は手続き先頭の以下。
(cond ((null? exp) #t)
リスト全部見終わっても何もナシであれば #f じゃねぇか、という事で #f 戻す形にしたら試験パス。いやはや。
現時点の実装は以下。
(define occurs-free? (lambda (var exp) (cond ((null? exp) #f) ((symbol? exp) (eqv? exp var)) ((eqv? (car exp) 'lambda) (and (not (let f ((l (cadr exp))) (cond ((null? l) #f) ((eqv? (car l) var) #t) (else (f (cdr l)))))) (occurs-free? var (cddr exp)))) (else (or (occurs-free? var (car exp)) (occurs-free? var (cdr exp)))))))
あと追加した試験を以下に。
(test-section "ex.1-22") (test* "(occurs-free? 'x '((lambda (x y) (+ x y)) x z)) should return #t" #t (occurs-free? 'x '((lambda (x y) (+ x y)) x z))) (test* "(occurs-free? 'y '((lambda (x y) (+ x y)) x z)) should return #f" #f (occurs-free? 'y '((lambda (x y) (+ x y)) x z))) (test* "(occurs-free? 'y '((lambda (x y) (+ x y)) x y)) should return #t" #t (occurs-free? 'y '((lambda (x y) (+ x y)) x y))) (test* "(occurs-free? 'x '((lambda (x y) (+ x y)) x y)) should return #t" #t (occurs-free? 'x '((lambda (x y) (+ x y)) x y)))
もう少し複雑なの、と思ったんですが引数と lambda な手続きしか増やせんのか。しかも symbol しか駄目だし。せめてもう一段 lambda をネストさせてみるか。
(occurs-free? 'x '((lambda (x y z) (((lambda (x) x) +)) x y z) a b x))
むむ。一応パスしてますが ...
上記で言えば、+ とか a とか b は occurs free ですか。他はそうではないのか。試験でも確認済み。次は occurs-bound? ッスか。試験は occurs-free? のをコピる。
でっち上がったのは以下。
(define occurs-bound? (lambda (var exp) (cond ((null? exp) #f) ((symbol? exp) #f) ((eqv? (car exp) 'lambda) (or (occurs-bound? var (cddr exp)) (and (let f ((l (cadr exp))) (cond ((null? l) #f) ((eqv? (car l) var) #t) (else (f (cdr l))))) (occurs-free? var (cddr exp))))) (else (or (occurs-bound? var (car exp)) (occurs-bound? var (cdr exp)))))))
がしかし試験が NG
<ex.1-22>---------------------------------------------------------------------- test (occurs-bound? 'x '((lambda (x y) (+ x y)) x z)) should return #f, expects #f ==> ERROR: GOT #t test (occurs-bound? 'y '((lambda (x y) (+ x y)) x z)) should return #t, expects #t ==> ok test (occurs-bound? 'y '((lambda (x y) (+ x y)) x y)) should return #f, expects #f ==> ERROR: GOT #t test (occurs-bound? 'x '((lambda (x y) (+ x y)) x y)) should return #f, expects #f ==> ERROR: GOT #t test (occurs-bound? 'x '((lambda (x y z) (((lambda (x) x) +) x y z) a b x)) should return #f, expects #f ==> ERROR: GOT #t test (occurs-bound? '+ '((lambda (x y z) (((lambda (x) x) +) x y z) a b x)) should return #f, expects #f ==> ok test (occurs-bound? 'a '((lambda (x y z) (((lambda (x) x) +) x y z) a b x)) should return #f, expects #f ==> ok test (occurs-bound? 'b '((lambda (x y z) (((lambda (x) x) +) x y z) a b x)) should return #f, expects #f ==> ok test (occurs-bound? 'y '((lambda (x y z) (((lambda (x) x) +) x y z) a b x)) should return #t, expects #t ==> ok test (occurs-bound? 'z '((lambda (x y z) (((lambda (x) x) +) x y z) a b x)) should return #t, expects #t ==> ok
うーん。これ、lambda 式の body で var が出たら #t が戻らないと駄目なカンジなんですが良いのでしょうか。