2011-06-01 147 views
2

我正在尝试在Lisp中编写一个使用自身重新实现let的宏。这是一个没有实际目的的平凡练习;然而,在给出一个response相关的问题后,我意识到我应该更多地了解宏。他们被吹捧为关于Lisp的伟大事情之一,但我很少使用它们。Lisp宏的问题

无论如何,这是我第一次尝试:

(defmacro mylet (args &rest exp) `(let ,args (dolist (x ,exp) x))) 

但是当我尝试类似:

(mylet ((a 5) (b 2)) (print (+ a b))) 

此抛出了一个错误:

#1=(PRINT (+ A B)) is not a symbol or lambda expression in the form (#1#) . 

ARGS(一和b)设置正确,但打印语句不起作用。我认为这是因为我使用了两个间接层次 - 指的是我在宏中创建的一个变量。但我似乎无法弄清楚如何解决它!有任何想法吗?

回答

4

您的宏扩展为:

(LET ((A 5) (B 2)) 
    (DOLIST (X ((PRINT (+ A B)))) X)) 

这是无效的,因为((PRINT (+ A B)))不是有效的表达。还有一个问题是,在宏扩展中使用实名符号可能导致变量捕获,但这不直接相关(请参阅PCL中的更多内容)。

在这里使用DOLIST是不必要的,而且编译得到正确的(您必须将所有子表单转换为匿名函数才能将它们粘贴到列表中,然后按顺序对其进行调用,然后存储最终结果以符合PROGN行为)。你可以使用progn这个,或者,因为LET包括隐含progn这个,只是拼接使用反引号机制,@功能体:

(defmacro mylet (args &body exp) `(let ,args ,(cons 'progn exp))) 

(defmacro mylet (args &body exp) `(let ,args ,@exp)) 
+0

谢谢,这是从来没有想过使用cons + progn这个有很大的answer--我不知道拼接。但是,我没有看到你在哪里((print(+ a b)))...它不应该试图评估(print(+ a b))吗?我错过了什么?即使没有dolist,像(first,exp)这样简单的东西也不起作用。 – Jeff 2011-06-01 18:37:54

+0

我从[宏展开器](http://www.lispworks.com/documentation/HyperSpec/Body/f_mexp_.htm)获得了'((print(+ ab))',这是测试宏是否扩展为你认为他们做了什么&rest /&body(这些是相同的,不同之处只对读者有意义)参数始终是所有剩余参数的列表,因此在宏展开时变量'exp'的值是打印表示'((print(+ ab))',它被按原样插入到结果代码中,然后代码就像输入代码一样执行。 – Ramarren 2011-06-01 19:09:21

+0

哎呀,应该是'那么它会被当作一个列表来处理......但即使存在,它仍然不起作用 – Jeff 2011-06-01 19:32:39