在lisp中将"when"写为宏函数

Jon*_*h P 2 clisp common-lisp

我想写会起作用方法一样微距功能时, Common Lisp中:

(defmacro when2 (&rest args)   
   `(if  (car (quote ,args)) (progn (quote ,args)) nil)
)
Run Code Online (Sandbox Code Playgroud)

但它远非像原来的运行:

> (when2 nil 2 3 4)
nil
> (when2 1 2 3 4)
(1 2 3 4)
> (when2 (< 1 2) 2 3 4)
((< 1 2) 2 3 4)
Run Code Online (Sandbox Code Playgroud)

我的错误来源是什么?

谢谢!

编辑:在siehe-falz的回复之后,我试过了

(macroexpand '(when2 1 2 3 4))
Run Code Online (Sandbox Code Playgroud)

这给了

(if 1 (progn '(2 3 4)) nil) 
Run Code Online (Sandbox Code Playgroud)

并让我意识到我不应该在预测之后得到那个引用和那些括号.

所以,我想出来了

(defmacro when2 (test &body args)   
   `(if  ,test (progn ,args) nil)
)
Run Code Online (Sandbox Code Playgroud)

但它仍然给我这个错误,我不明白为什么:

*** - eval: 2 is not a function name; try using a symbol instead
Run Code Online (Sandbox Code Playgroud)

cor*_*ump 7

宏转换代码,代码是数据.

让我们看看我们如何手动转换代码以及宏如何帮助简化代码.

这是您要转换的列表:

(when2 test 1 2 3 4)
Run Code Online (Sandbox Code Playgroud)

它是6个原子值的列表,是符号和整数的混合.我们叫这个清单input.

在这里,我们想要生成这个代码:

(if test (progn 1 2 3 4) nil)
Run Code Online (Sandbox Code Playgroud)

提取价值

来自input:

  • 您可以test通过获取其second值来访问.
  • 你可以(1 2 3 4)通过cdr两次访问(即cddr)

建筑代码

知道这一点,您可以构建结果列表:

(let ((input '(when2 test 1 2 3 4)))
  (list 'if
        (second input)
        (list* 'progn (cddr input))
        nil))

=> (if test (progn 1 2 3 4) nil)
Run Code Online (Sandbox Code Playgroud)

我曾经LIST*建立一个PROGN以另一个列表开头的列表.

一般解决方案

在上面的示例中,您可以看到我们不太依赖输入列表的确切内容,而只是它的一般形状.实际上,我们可以创建input一个参数,并获得我们想要的转换函数:

(lambda (input)
  (list 'if
        (second input)
        (list* 'progn (cddr input))
        nil))
Run Code Online (Sandbox Code Playgroud)

反引号

反引用语法是编写上述内容的较短方式; 不会对反引号代码进行求值,并将其作为模板放置在其中放置已计算的表达式(有点像字符串插值,除了saner).您从引用切换到评估表单:

  • `(a ,b) 相当于 (list 'a b)
  • `(a b ,@list e f),使用list列表(c d)给出(a b c d e f),将所有元素放在list与其周围相同的级别.这是拼接运算符.

我们可以写下:而不是上面的函数:

(lambda (input)
  `(if ,(second input) (progn ,@(cddr input)) nil))
Run Code Online (Sandbox Code Playgroud)

解构代码

通过提供功能强大的Macro Lambda列表,宏进一步简化了编写这些代码转换功能的过程.而不是显式调用(second input)or (cddr input),而是使用模式匹配; 你声明输入代码应该具有什么形状,并且宏将变量绑定到输入的每个子部分,同时转换代码:

(defmacro when2 (test &body expressions)
  `(if ,test (progn ,@expressions) nil))
Run Code Online (Sandbox Code Playgroud)

这里,宏的名称,它的参数和包含在其中的代码体很容易在扩展代码中声明和使用.见DEFMACRO.

效果与手动编写原子列表中的代码相同,但模式匹配语法和准引用语法都使编写宏更容易.


Mar*_*ann 5

不知道关于宏的太多了,我想你应该拼接args成的身体progn使用,@:

(defmacro when2 (test &body args)   
  `(if ,test (progn ,@args) nil))
Run Code Online (Sandbox Code Playgroud)

我建议您仔细阅读以下参考资料,以便更深入地了解CL中的宏.

http://www.gigamonkeys.com/book/macros-standard-control-constructs.html