macros - 在宏定义中混淆

标签 macros common-lisp sicp lazy-sequences

我想在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/

相关文章:

excel - UiPath - 是否可以将值从 UiPath 传递给 excel 宏?

C++ - 用于根据枚举类型调用函数的宏

lisp - 在 lisp 中,如何使用 floor 函数返回的第二个值?

java - 需要专门学习Scheme了解Java和Python

scheme - 什么是定点?

if 条件下的 C 宏

c++ - 预处理器宏扩展到另一个预处理器指令

haskell - Lisp 能否以不可变的函数式方式轻松使用?

递归进行和弦转位

scheme - (define (add x y) (+ x y)) 和 (define add (lambda (x y) (+ x y))) 有什么区别?