さかもとのブログ

つらつらと

SICP演習問題4.30

これはおもしろい問題.
しかし,問題の訳文がひどい.これは原書を読んだほうがわかりやすい.

序文だけ載せる.

Exercise 4.30. Cy D. Fect, a reformed C programmer, is worried that some side effects may never take place, because the lazy evaluator doesn't force the expressions in a sequence. Since the value of an expression in a sequence other than the last one is not used (the expression is there only for its effect, such as assigning to a variable or printing), there can be no subsequent use of this value (e.g., as an argument to a primitive procedure) that will cause it to be forced. Cy thus thinks that when evaluating sequences, we must force all expressions in the sequence except the final one. He proposes to modify eval-sequence from section 4.1.1 to use actual-value rather than eval:

(define (eval-sequence exps env)
  (cond ((last-exp? exps) (eval (first-exp exps) env))
        (else (actual-value (first-exp exps) env)
              (eval-sequence (rest-exps exps) env))))

始めはいまいちわからなかったので,いろいろ試したてやっとわかった.と思ったら,
参考はとてもわかりやすい.

a

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

(for-each (lambda (x) (newline) (display x))
          (list 1 2 3))

はどちらでも正しく動作する.
for-eachのprocはlambda文になり,lambdaはprimitive-procedure(基本手続き)なので,引数は遅延されず,通常通りにbodyが評価される.また,bodyの要素もどちらも基本手続きなので,通常通り評価され,displayが実行される.

おもしろいのはb

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

(p2 1)

これをもともとのeval-sequenceで実行すると,

1

となり,
Cyのeval-sequence, つまりactual-valueを使い,並びをすべてforceする場合

(1 2)

となる.
もともとのeval-sequenceでは,

(define (p e)
    e 
    x)

のeがforceされず,そのため,(p (set! …))が評価されず,xは1のままで,(p2 x)の副作用として1が表示される.Cyのeval-sequenceでは,並びをすべてforceするので, (p (set! …))が評価され,xの値が更新され,(p2 x)の副作用として(1 2)が表示される.
つまり,もともとのeval-sequenceでも,eがforceされるようにすれば,(1 2)が副作用の結果となる.
例えば,

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

のように,eを引数とするような基本手続きを使うと,eはforceされ,xは(1 2)となる.また,こんな例題も考えてみた.(一応結果も載せる)

;;; L-Eval input:
(define (hoge x y)
  (print 'hogehoge)
  x
  y)

;;; L-Eval value:
ok

;;; L-Eval input:
(define (p2 x)
  (define (p e)
    (hoge e 11)
    x)
  (p (set! x (cons x '(2)))))

;;; L-Eval value:
ok

;;; L-Eval input:
(p2 1)
hogehoge

;;; L-Eval value:
1

この場合,(hoge e 11)より,hogeを呼び出し,aと同様printは行われる.しかし,eはhogeに引数として渡しても,hogeでforceをすることがなく,xは1のままで,副作用の結果は,1のままである.