EoPL reading (28) 1.3.1 Free and Bound Variables

迷走の原因がなんとなくイメージできたような気がするので纏めてみます。
まず、occures free の方から。lambda 式 E において変数 x が occurs free であるための必要十分条件が以下。

  • E は変数の参照で E は x と同じである
  • E は (lambda (y) E') な式で y は x と異なり、E' において x は occurs free である
  • E は (E1 E2) な式で E1 又は E2 において x は occurs free である

これをコードにしたのが例示されている以下の手続き。

(define occurs-free?
  (lambda (var exp)
    (cond
      ((symbol? exp) (eqv? exp var))
      ((eqv? (car exp) 'lambda)
       (and (not (eqv? (caadr exp) var))
            (occurs-free? var (caddr exp))))
      (else (or (occurs-free? var (car exp))
                (occurs-free? var (cadr exp)))))))

上記、いくつか限定されている事項あり。

  • lambda 式の引数は一つ
  • lambda 式の body はリスト一発
  • リスト一発な lambda 式の body は以下限定
    • lambda 式
    • 要素が二つのリスト

これが Exercise-1.22 で

  • lambda 式の引数は複数要素を許容
  • lambda 式の body は複数要素を許容

という形になっているはず。この問題の解として以下の手続きをヒリ出してるんですが

(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)))))))

式が複数あった場合、or で連結されてるので、リストのケツに到達したら #f を戻して問題ないはず。lambda 式の引数のチェックとかも微妙なんですが、問題なのは lambda が入れ子になってるケイスではないか、という事に気づく。
あ、そうでもないか。var は持ち回ってるから大丈夫?
試験を見たところ、問題なく動作している模様。

(test* "(occurs-free? 'y '(lambda (y) ((lambda (x) x) y))) should return #f"
       #f
       (occurs-free? 'y '(lambda (y) ((lambda (x) x) y))))
(test* "(occurs-free? 'f '(lambda (f) (lambda (x) (f x)))) should return #f"
       #f
       (occurs-free? 'f '(lambda (f) (lambda (x) (f x)))))
(test* "(occurs-free? 'x '(lambda (f) (lambda (x) (f x)))) should return #f"
       #f
       (occurs-free? 'x '(lambda (f) (lambda (x) (f x)))))

あと、lambda な式の処理は以下に。

     ((eqv? (car exp) 'lambda)
      (and (let f ((l (cadr exp)))
	     (cond ((null? l) #t)
		   ((eqv? (car l) var) #f)
		   (else
		    (f (cdr l)))))
	   (occurs-free? var (cddr exp))))

もうすこし頑張ろう。occurs bound について。lambda 式 E において変数 x が occurs bound であるための必要十分条件が以下

  • E は (lambda (y) E') な式で
    • E' において x が occurs bound である
    • 又は x と y が同じであり y は E' において occurs free である
  • E は (E1 E2) な式で E1 又は E2 において x は occurs bound である

む。手続き定義は以下。

(define occurs-bound?
  (lambda (var exp)
    (cond
      ((symbol? exp) #f)
      ((eqv? (car exp) 'lambda)
       (or (occurs-bound? var (caddr exp))
           (and (eqv? (caadr exp) var)
                (occurs-free? var (caddr exp)))))
      (else (or (occurs-bound? var (car exp))
                (occurs-bound? var (cadr exp)))))))

うーん。これって微妙にダウトなカンジ。定義によれば lambda 式のナニは以下?

      ((eqv? (car exp) 'lambda)
       (or (occurs-bound? var (caddr exp))
           (and (eqv? (caadr exp) var)
                (occurs-free? (caadr exp) (caddr exp)))))

あ、same variable なので var でも OK ですな。これも occurs free と同様のシバリが入った定義になっているはず。で、exercise-1.22 ででっちあげたのが以下。

(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)))))))

ここで言えば得心してないのは lambda body が occurs free な事、という部分。こんな試験を書いてます。全部 #t が戻ってくるあたりは微妙。

(test* "(occurs-bound? 'y '(lambda (y) ((lambda (x) x) y))) should return #t"
       #t
       (occurs-bound? 'y '(lambda (y) ((lambda (x) x) y))))
(test* "(occurs-bound? 'f '(lambda (f) (lambda (x) (f x)))) should return #t"
       #t
       (occurs-bound? 'f '(lambda (f) (lambda (x) (f x)))))
(test* "(occurs-bound? 'x '(lambda (f) (lambda (x) (f x)))) should return #t"
       #t
       (occurs-bound? 'x '(lambda (f) (lambda (x) (f x)))))

上記の上二つは lambda body の中で occurs free です。最後のヤツは cddr が occurs bound なパターン。やはりちょっと試験が足らないですね。
Exercize-1.22 について、試験を書きなおします。とりあえずエントリ投入。