首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用宏展开-1在let窗体中展开宏(实用的通用Lisp,第8章,“堵塞漏洞”)

使用宏展开-1在let窗体中展开宏(实用的通用Lisp,第8章,“堵塞漏洞”)
EN

Stack Overflow用户
提问于 2021-11-21 16:08:14
回答 2查看 138关注 0票数 2

在实用的通用Lisp第8章中,“堵塞漏洞”定义了这个宏,并发现它通过macroexpand-1的检查而泄漏。

代码语言:javascript
复制
(defmacro do-primes ((var start end) &body body)
  `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
        (ending-value ,end))
       ((> ,var ending-value))
     ,@body))

(以下看似无辜的呼吁)-primes由于泄漏而无法正常工作:

(do-素数(结束值0 10) (打印结束值-值))这一项也不是:(let ((结束值0)) (do-素数(p 0 10) (incf结束值p))结束值)

我可以在这样的地方使用macroexpand-1,我已经将这个宏命名为

代码语言:javascript
复制
(macroexpand-1 '(do-primes4 (ending-value 0 10)
  (print ending-value)))

(DO ((ENDING-VALUE (NEXT-PRIME 0) (NEXT-PRIME (1+ ENDING-VALUE)))
     (ENDING-VALUE 10))
    ((> ENDING-VALUE ENDING-VALUE))
  (PRINT ENDING-VALUE))

但是,我不能在第二种形式上这样做,在第二种形式中,宏调用使用let

代码语言:javascript
复制
(macroexpand-1 '(let ((ending-value 0))
  (do-primes4 (p 0 10)
    (incf ending-value p))
  ending-value))

(LET ((ENDING-VALUE 0))
  (DO-PRIMES4 (P 0 10)
    (INCF ENDING-VALUE P))
  ENDING-VALUE)

它似乎并不是真正的宏观扩张做前4。我用的是SBCL。

EN

回答 2

Stack Overflow用户

发布于 2021-11-21 17:15:39

有两个宏扩展函数,macroexpandmacroexpand-1http://clhs.lisp.se/Body/f_mexp_.htm.这两个扩展宏,macroexpand-1做一个级别,macroexpand继续进行直到它被完全展开。两者都返回两个值:

  1. 展开
  2. 标志,该标志指示展开发生

宏观扩展如下:

代码语言:javascript
复制
; macro expansion

(macroexpand-1 '(do-primes4 (ending-value 0 10)
   (print ending-value)))
(DO ((ENDING-VALUE (NEXT-PRIME 0) (NEXT-PRIME (1+ ENDING-VALUE)))
     (ENDING-VALUE 10))
((> ENDING-VALUE ENDING-VALUE))
  (PRINT ENDING-VALUE))

; flag
T

首先打印展开,然后再打印标志--这里是T,以显示展开发生了。

第二个例子的问题是,let是宏扩展中的第一个项目。目前,您只尝试展开let表单,其主体未更改地返回:

代码语言:javascript
复制
(macroexpand-1 '(let ((ending-value 0)) (do-primes4 (ending-value 0 10)
                                               (print ending-value))))

(LET ((ENDING-VALUE 0))
   (DO-PRIMES4 (ENDING-VALUE 0 10)
      (PRINT ENDING-VALUE)))
NIL

该标志接受值NIL,以显示没有展开。

let窗体扩展到自身,因为它不是带有标志NIL的宏

代码语言:javascript
复制
CL-USER> (macroexpand-1 '(let ((x 0))))
  (LET ((X 0)))
  NIL
票数 1
EN

Stack Overflow用户

发布于 2021-11-22 11:46:28

虽然,正如Rainer所指出的,在这种情况下,您需要一个代码遍历器来查看扩展的实际内容,而且macroexpand-1多的macroexpand都不是代码遍历者,但是您实际上并不需要这样做来查看这里的问题所在。以类似的形式

代码语言:javascript
复制
(let ((ending-value 0))
  (do-primes (p 0 10)
    (incf ending-value p))
  ending-value)

您只需在顶层展开do-primes表单,然后替换它,得到明显的结果:

代码语言:javascript
复制
(let ((ending-value 0))
  (do ((p (next-prime 0) (next-prime (1+ p))) (ending-value 10))
      ((> p ending-value))
    (incf ending-value p))
  ending-value)

现在马上就可以看出问题是什么:您正在递增的ending-value不是您认为的绑定。

有各种各样的CL代码-步行者在那里:我不太熟悉他们推荐一个。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70056233

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档