4

我想在 SICP 3.5.1 节中实现惰性流

首先,我定义了这两个函数

(defmacro delay (form)
  `(lambda () ,form))

(defun force (form)
  (when form
    (funcall form)))

当我们打电话时:

(force (delay '(+ 1 2)))
;;=> (+ 1 2)

(force (delay (+ 1 2)))
;;=> 3

所以这行得通。然后我继续定义'stream-cons',但这一次似乎有两种方法:

(defmacro stream-cons (a b)
  `(cons ,a ,(delay b)))

(defmacro stream-cons (a b)
  `(cons ,a (delay ,b)))

我不认为他们是不同的,但我错了!第一版,这是一个错误的版本,当调用时:

(force (cdr (stream-cons 'a (progn (print "hello") 2))))
;;=> (PROGN (PRINT "hello") 2)

(macroexpand '(stream-cons 'a (progn (print "hello") 2)))
;;=> (CONS 'A #<CLOSURE (LAMBDA # :IN STREAM-CONS) {25ABB3A5}>)

和第二版,这是正确的,当被称为:

(force (cdr (stream-cons 'a (progn (print "hello") 2))))
;; 
;; "hello" 
;; => 2

(macroexpand '(stream-cons 'a (progn (print "hello") 2)))
;;=> (CONS 'A (DELAY (PROGN (PRINT "hello") 2)))

现在,我很困惑。谁能帮我弄清楚两者的不同之处?非常感谢!

我的环境:Windows 32 位,SBCL 1.1.4

4

1 回答 1

7

这是理解宏的一个重要概念。

事情是,(delay b)在宏扩展时评估的,即lambda在适当的位置创建并包裹在传递给它的文字值上,即符号列表。所以你得到一个总是返回相同值的常量函数——一个恰好是你的代码的列表。

你可以像这样描绘它:

,(delay '(progn (print "hello") 2)) => (lambda () '(progn (print "hello") 2))

在第二个变体中(delay ,b)

(delay ,'(progn (print "hello") 2)) => (delay (progn (print "hello") 2))

这里的问题是宏调用的参数是按字面意思传递的,没有求值。因此delay有效地接收到引用列表 ( '(progn (print "hello") 2))。如果他们见面,逗号的作用是取消报价。

于 2013-05-30T05:15:47.487 回答