在 lisp 中将 "when" 写成宏函数
Writing "when" as a macro function in lisp
我正在尝试编写一个宏函数,其功能与 Common Lisp 中的 when 相同:
(defmacro when2 (&rest args)
`(if (car (quote ,args)) (progn (quote ,args)) nil)
)
但它的功能远不如原来的 when:
> (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)
我的错误来源是什么?
谢谢!
编辑:经过siehe-falz的回复,
我试过了
(macroexpand '(when2 1 2 3 4))
这给了
(if 1 (progn '(2 3 4)) nil)
让我意识到我不应该在 progn 之后得到那个引用和那些括号。
所以,我想到了
(defmacro when2 (test &body args)
`(if ,test (progn ,args) nil)
)
但它仍然给我这个错误,我不明白为什么:
*** - eval: 2 is not a function name; try using a symbol instead
使用macro-expand
调试您的宏。例如
(macroexpand '(when2 nil 2 3 4))
也许您可以像这样编写宏来简化您的任务:
(defmacro when2 (test &rest args) ...)
在不太了解宏的情况下,我认为您应该使用 ,@
:
将 args
拼接到 progn
的主体中
(defmacro when2 (test &body args)
`(if ,test (progn ,@args) nil))
我建议您仔细阅读以下参考资料,以便更深入地了解 CL 中的宏。
http://www.gigamonkeys.com/book/macros-standard-control-constructs.html
宏转换代码,代码即数据。
让我们看看我们如何手动转换代码以及宏如何帮助简化代码。
例子
这是您要转换的列表:
(when2 test 1 2 3 4)
它是一个包含 6 个原子值的列表,是符号和整数的混合。
我们称此列表为 input
.
在这里,我们要生成此代码:
(if test (progn 1 2 3 4) nil)
提取值
来自 input
:
- 您可以通过获取其
second
值来访问 test
。
- 您可以通过
cdr
两次(即 cddr
)访问 (1 2 3 4)
建筑规范
了解这一点,您可以构建结果列表:
(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
。
效果与手动从原子列表构建代码相同,但模式匹配语法和准引用语法都使编写宏更容易。
我正在尝试编写一个宏函数,其功能与 Common Lisp 中的 when 相同:
(defmacro when2 (&rest args)
`(if (car (quote ,args)) (progn (quote ,args)) nil)
)
但它的功能远不如原来的 when:
> (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)
我的错误来源是什么?
谢谢!
编辑:经过siehe-falz的回复, 我试过了
(macroexpand '(when2 1 2 3 4))
这给了
(if 1 (progn '(2 3 4)) nil)
让我意识到我不应该在 progn 之后得到那个引用和那些括号。
所以,我想到了
(defmacro when2 (test &body args)
`(if ,test (progn ,args) nil)
)
但它仍然给我这个错误,我不明白为什么:
*** - eval: 2 is not a function name; try using a symbol instead
使用macro-expand
调试您的宏。例如
(macroexpand '(when2 nil 2 3 4))
也许您可以像这样编写宏来简化您的任务:
(defmacro when2 (test &rest args) ...)
在不太了解宏的情况下,我认为您应该使用 ,@
:
args
拼接到 progn
的主体中
(defmacro when2 (test &body args)
`(if ,test (progn ,@args) nil))
我建议您仔细阅读以下参考资料,以便更深入地了解 CL 中的宏。
http://www.gigamonkeys.com/book/macros-standard-control-constructs.html
宏转换代码,代码即数据。
让我们看看我们如何手动转换代码以及宏如何帮助简化代码。
例子
这是您要转换的列表:
(when2 test 1 2 3 4)
它是一个包含 6 个原子值的列表,是符号和整数的混合。
我们称此列表为 input
.
在这里,我们要生成此代码:
(if test (progn 1 2 3 4) nil)
提取值
来自 input
:
- 您可以通过获取其
second
值来访问test
。 - 您可以通过
cdr
两次(即cddr
)访问(1 2 3 4)
建筑规范
了解这一点,您可以构建结果列表:
(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
。
效果与手动从原子列表构建代码相同,但模式匹配语法和准引用语法都使编写宏更容易。