我想在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
最佳答案
这是理解宏的一个重要概念。
问题是,(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)
)。逗号的作用是在他们相遇时取消报价。
关于macros - 在宏定义中混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16827583/