写作"当"作为lisp中的宏函数

时间:2017-11-18 03:16:45

标签: common-lisp clisp

我试图编写一个宏函数,其功能与Common Lisp中的 时相同:

> (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)

但它的功能远不如原来的

(macroexpand '(when2 1 2 3 4))

我的错误来源是什么?

谢谢!

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

(if 1 (progn '(2 3 4)) nil) 

给了

(defmacro when2 (test &body args)   
   `(if  ,test (progn ,args) nil)
)

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

所以,我想出了

*** - eval: 2 is not a function name; try using a symbol instead

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

stage('test') {
  sh (
    '''cd $WORKSPACE
        make test
    '''
  )
}

3 个答案:

答案 0 :(得分:7)

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

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

实施例

以下是您要转换的列表:

(when2 test 1 2 3 4)

它是6个原子值的列表,是符号和整数的混合。 我们将此列表称为input

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

(if test (progn 1 2 3 4) nil)

提取值

来自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)

我使用LIST*来构建一个以PROGN开头的列表,然后是另一个列表。

一般解决方案

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

(lambda (input)
  (list 'if
        (second input)
        (list* 'progn (cddr input))
        nil))

反引号

反引用语法是编写上述内容的较短方式;该 不评估反引号代码,并作为您的模板 放置被评估的表达式(有点像字符串 插值,除了saner)。您可以从引用形式切换到评估形式:

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

代替上述功能,我们可以写:

(lambda (input)
  `(if ,(second input) (progn ,@(cddr input)) nil))

解构代码

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

(defmacro when2 (test &body expressions)
  `(if ,test (progn ,@expressions) nil))

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

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

答案 1 :(得分:5)

在不了解宏的情况下,我认为您应该使用argsprogn拼接到,@的正文中:

(defmacro when2 (test &body args)   
  `(if ,test (progn ,@args) nil))

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

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

答案 2 :(得分:3)

使用macro-expand调试您的宏。例如

(macroexpand '(when2 nil 2 3 4))

也许你可以像这样编写宏来简化你的任务:

(defmacro when2 (test &rest args) ...)