通常の評価規則で(p)
は、引数なしで関数を呼び出すことによって評価されp
ます。たとえば (Common Lisp の場合):
> (defun p ()
5)
=> P
> (p)
=> 5
あなたの質問は、「遅延評価」と呼ばれるものに言及することから始まりました。Common Lisp はデフォルトではこれを行いません。関数のすべての引数を左から右に評価します。スキームは、それらが評価される順序を指定していません。
ただし、物事を評価する前に、展開する必要があります (これは、Lisp では多くのことを意味します)。これにより、Lisp は評価の順序を制御できます。たとえばp
、マクロである可能性があります。この場合、通常の評価ルールは必ずしも適用されません。繰り返しますが、Common Lisp では次のようになります。
> (defmacro p ()
(print "I'm being expanded!") ; print on expansion
(terpri) ; new line.
`(progn (print "I'm being evaluated!") ; print on evaluation
(terpri)
5))
=> P
これは、読み取り評価印刷ループに入ります。式が読み取られ、展開され、評価され、出力されます。
> (p)
I'm being expanded!
I'm being evaluated!
=> 5
展開の評価を停止するには、ラムダに貼り付けましょう。拡張メッセージがまだ出力されていることに気付くでしょう。
> (defvar foo (lambda () (p)))
I'm being expanded!
=> COMPILED FUNCTION #<LAMBDA>
これを呼び出すと、フォームが評価されます。
> (funcall foo) ; call the function
I'm being evaluated!
=> 5
マクロ呼び出しを単独で展開できます。
> (macroexpand-1 '(lambda () (p)))
I'm being expanded!
=> (lambda () (progn (print "I'm being evaluated!")
(terpri)
5))
Haskell などの言語には、デフォルトで遅延評価があります。あなたが引用した一節で、SICP は Lisp の怠惰なバージョンを想像させます。このような Lisp が機能し、必要なものだけを評価するには、値に到達するまですべてをやみくもに展開して評価するだけでなく (SICP の置換モデルの説明を参照)、単に展開し、値を具体的に求められた場合にのみ、物事を評価します。P
これを、ラムダ式の本体でマクロを展開し、値が必要なときに評価を強制した上記の例と比較できFUNCALL
ます。SICP の後半で、同様の手法を使用して遅延リストを実装します。
私が言ったように、Haskell はこの種のことを自動的に行います。同じことを行う Lisp を想像することは難しくありません (ただし、現在、一般的な Lisp はそうしていません)。本の実際の質問を考えると、私は少し話を逸らしたと思いますが、何が評価され、いつ評価されるか、そしてそれがもたらす違いについて少し理解していただければ幸いです。