Common lisp 避免在lisp中实现宏函数的副作用
我试图在Lisp中实现宏函数Common lisp 避免在lisp中实现宏函数的副作用,common-lisp,Common Lisp,我试图在Lisp中实现宏函数或 我的尝试: (defmacro or2 (test &rest args) `(if ,test ,test (if (list ,@args) (or2 ,@args) nil)) ) 但是,如果我用这样的方法进行测试: (or2 (print 1) 2 ) 1 1 1 而对于默认的或: (or (print 1) 2) 1 1 我知道这是因为我在if子句的开头有两个,test,但我不知道如何避免它。我如何避免应用两次测试效
或
我的尝试:
(defmacro or2 (test &rest args)
`(if ,test ,test (if (list ,@args) (or2 ,@args) nil)) )
但是,如果我用这样的方法进行测试:
(or2 (print 1) 2 )
1
1
1
而对于默认的或:
(or (print 1) 2)
1
1
我知道这是因为我在if
子句的开头有两个,test
,但我不知道如何避免它。我如何避免应用两次测试效果?如果必须手动编写代码,您将如何解决副作用问题
(or2 (print 1) 2)
中间变量
很可能,您会这样做:
(let ((value (print 1)))
(if value value 2))
您需要定义一个局部变量来保存第一个表达式的值,以便以后可以引用该变量,而不是多次重新计算同一个表达式
但是,如果在展开代码的词法上下文中已经有一个名为value
的变量,该怎么办?如果您引用的不是2
,而是另一个值
,该怎么办?这个问题称为变量捕获
根森
在CommonLisp中,您引入了一个新的符号,它保证不会绑定到任何东西,使用
递归展开
上述操作与直接编写,args
相同
但您混淆了宏扩展和执行时间。如果直接在代码中插入args
,则将对其进行评估(很可能,这将作为错误的函数调用失败)。相反,您需要测试在宏扩展期间args
是否为非null。
此外,您可能应该首先测试表达式列表是否包含多个元素,以简化生成的代码。
粗略地说,您必须考虑以下情况:
(or2)
为nil
(或2 exp)
与exp
(或2 exp&rest args)
与以下相同,其中var
是一个新符号:
`(let ((,var ,exp))
(if ,var ,var (or2 ,@args)))
请使用宏扩展-1
:
(宏扩展-1'(or2(打印1)2))
; ==> (如果(打印1)(打印1)(如果(列表2)(或2)无));
; ==> T
对于宏,您希望计算顺序符合预期,并且希望表达式只计算一次。因此,扩张应该是这样的:
(or2 (print 1) 2 )
1
1
1
(let((tmp(打印1)))
(如果是tmp
tmp
(或2)
而tmp
应该是由gensym
生成的符号。另外,当args
为nil
时,您应该将或2
展开为仅测试:
(defmacro or2(测试和rest参数)
(如果(endp args)
测试
let((tmp(gensym“tmp”))
`(let(,tmp,测试))
(如适用,tmp
,tmp
(或2,@args()()))
您可以简化此操作:
(defmacro or2(测试和rest参数)
(如果(endp args)
测试
(仅一次(测试)
`(如果有,测试
,测试
(or2,@args(()))
我已经累了好几个小时了;但我不知道如何将答案的所有要素组合在一起才能使其发挥作用:(@JonathP很抱歉听到这个消息。我想说西尔维斯特的回答给出了我心目中的那种代码。但是如果你被卡住了,你可以在你的问题中提供更多的细节,以及你目前掌握的代码,我们可以看到是什么阻碍了你。我尝试了几种方法(没有全部保留副本).我添加了可选的测试和一些打印,以了解发生了什么。这是我最新的跛脚尝试
`(let ((,var ,exp))
(if ,var ,var (or2 ,@args)))