xie*_*pan 4 macros common-lisp sicp lazy-sequences
我想在SICP第3.5.1节中实现延迟流
首先,我定义了这两个功能
(defmacro delay (form)
`(lambda () ,form))
(defun force (form)
(when form
(funcall form)))
Run Code Online (Sandbox Code Playgroud)
当我们打电话:
(force (delay '(+ 1 2)))
;;=> (+ 1 2)
(force (delay (+ 1 2)))
;;=> 3
Run Code Online (Sandbox Code Playgroud)
这样才有用.然后我继续定义`stream-cons',但这次似乎有两种方式:
(defmacro stream-cons (a b)
`(cons ,a ,(delay b)))
(defmacro stream-cons (a b)
`(cons ,a (delay ,b)))
Run Code Online (Sandbox Code Playgroud)
我不认为他们是不同的,但我错了!第一版,这是一个错误的版本,当被称为:
(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}>)
Run Code Online (Sandbox Code Playgroud)
和第二版,这是正确的,当被称为:
(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)))
Run Code Online (Sandbox Code Playgroud)
现在,我很困惑.谁能帮助我弄清楚两者的不同之处?非常感谢!
我的环境:Windows 32位,SBCL 1.1.4
这是了解宏的一个重要概念.
事情是,(delay b)在宏扩展时进行评估,即lambda在适当的位置创建并包裹在传递给它的文字值(即符号列表)上.因此,您将获得一个始终返回相同值的常量函数 - 恰好是您的代码的列表.
你可以这样画:
,(delay '(progn (print "hello") 2)) => (lambda () '(progn (print "hello") 2))
Run Code Online (Sandbox Code Playgroud)
在第二个变体中(delay ,b):
(delay ,'(progn (print "hello") 2)) => (delay (progn (print "hello") 2))
Run Code Online (Sandbox Code Playgroud)
这里的问题是宏调用的参数是字面上传递的,没有评估.因此,delay有效地接收引用列表('(progn (print "hello") 2)).如果他们见面,逗号做的是取消引用.