SICP ゼミ第55回

練習問題4.30

a .

(for-each (lambda (x) (newline) (display x))
  (list 57 321 88))

を呼ぶ。 問題になっているのは eval-sequence に食われる begin 節、すなわち

(define (for-each proc items)
  (if (null? items)
    'done
    (begin (proc (car items ))
           (for-each proc (cdr items )))))

から

(begin ((lambda (x) (newline) (display x)) 57)
        (for-each proc (list 321 88))))

のところである。 副作用がないという主張に基づくならば、このとき lambda 節が force されず、結局 list の最後のである88だけが評価されてしまうということになる。 しかし実際には、lambda 節の中にある演算子であるnewline と display が基本手続きであるため、これは force され正しい出力がなされる。

b . 結果としては

(define (p1 x)
  (set! x (cons x '(2)))
  x)

の出力は (1 . 2)

(define (p2 x)
  (define (p e)
    e
    x)
  (p (set! x (cons x '(2)))))

の出力は 1 である。

p1 については

(set! x (cons x '(2)))
x

のシーケンスが評価される。 まず一つ目の要素である set! が評価されるが、この時 set! が基本手続きであることからこれは force され、xに(1 . 2)が代入される。その後 (x) が評価されるので正しくペアが返ってくる。

しかし p2 については、まず (p (set! x (cons x '(2))))が評価されるが、p が基本手続きでないため、引数の set! 節が force されないまま thunk として残っている環境となる。 その後

e
x

のシーケンスが評価されるが、このとき、e には thunk しか入っていない。その後 x が評価されるときに e は呼ばれない。このため単純に x が呼ばれて 1 が返ってくる。

c . シーケンスの中身を順番に force していくだけなので問題ない。

d . 純粋に個人の好み。本文中の eval-sequence は、シーケンスであろうがなかろうが遅延評価なのだから必要になるまでシーケンスの要素を force するべきではないという考えに基づいたもの。Cy の eval-sequence は遅延評価であってもシーケンスは前から順に評価を値まで完了させるべきという考えに基づいたものである。 ぼくは Cy のほうが好きです。

by tube

練習問題4.31

最初に実装したevalと,evalを遅延評価ができるように改造したeval-delayと,メモ化もできるように改造したeval-delay-memoの3つを用意しておいて,引数の後ろにlazyが来ているか,lazy-memoが来ているか,何もひっついていないかでそれぞれ別々のevalを呼び出せば良い.実装は闇だからやらない.

速水奏さんと鷺沢文香さんを応援しています.