构建评估的评估是否等于宏?

时间:2014-06-04 15:28:36

标签: macros lisp common-lisp evaluation

我想知道这两个nth的定义是否相等:

予。被定义为宏:

(defmacro -nth (n lst)
  (defun f (n1 lst1)
    (cond ((eql n1 0) lst1)
      (t `(cdr ,(f (- n1 1) lst1)))))
  `(car ,(f n lst)))

II。被定义为一堆函数:

(defun f (n lst)
  (cond ((eql n 0) lst)
        (t `(cdr ,(f (- n 1) lst)))))
(defun f1 (n lst)
  `(car ,(f n `',lst)))
(defun --nth (n lst)
  (eval (f1 n lst)))

我有正确的想法吗?宏定义是评估表达式,在其体内构建吗?

2 个答案:

答案 0 :(得分:4)

好的,让我们从头开始。

宏用于创建通常依赖于宏输入的新表单。在编译或评估代码之前,必须展开。扩展宏是在评估使用它的表单之前发生的过程。这种扩张的结果通常是一种lisp形式。

因此,在宏中这里有几个级别的代码。

  • 在宏扩展期间(不是在运行时!)将评估未引用的代码,在您的示例中,在扩展宏时定义函数f(为什么?);
  • 接下来引用(通常引用或反引号或甚至嵌套的反引号)代码,它们将成为宏展开结果的一部分(以其字面形式);您可以控制在宏展开期间将评估哪部分代码以及将保持完整(引用,部分或完全)的代码。这允许人们在执行之前构造任何东西。

宏的另一个特性是它在扩展之前不会评估它的参数,而函数则会。为了让你了解什么是宏,请看这个(想到的第一件事):

(defmacro aif (test then &optional else)
  `(let ((it ,test))
     (if it ,then ,else)))

你可以像这样使用它:

CL-USER> (defparameter *x* '((a . 1) (b . 2) (c . 3) (d . 4)))
*X*
CL-USER> (aif (find 'c *x* :key #'car) (1+ (cdr it)) 0)
4

此宏创建有用的词法绑定,捕获变量it。在检查条件后,您不必重新计算结果,它可以在表格中访问,然后是'和其他'。它只是一个函数是不可能的,它已经在语言中引入了新的控件构造。但宏不仅仅是创造词汇环境。

宏是一个强大的工具。要完全描述你可以用它做什么是不可能的,因为你可以做任何事情。但nth不是你需要宏的东西。要构建nth的克隆,您可以尝试编写递归函数。

重要的是要注意LISP宏是编程世界中最强大的东西,而LISP是唯一拥有这种能力的语言; - )

为了激励你,我会推荐这篇文章:http://www.paulgraham.com/avg.html

要掌握宏,请从以下内容开始:

http://www.gigamonkeys.com/book/macros-defining-your-own.html

然后可能是Paul Graham" On Lisp"然后"让Over Lambda"。

答案 1 :(得分:1)

无需宏和eval进行抽象以获取列表的第n个元素。除非索引是文字编号,否则您的宏-nth甚至不起作用。试试这个:

(defparameter test-list '(9 8 7 6 5 4 3 2 1 0))
(defparameter index 3)

(nth index test-list)  ; ==> 6 (this is the LISP provided nth)
(-nth index test-list) ; ==> ERROR: index is not a number

第n个典型的递归解决方案:

(defun nth2 (index list)
  (if (<= index 0) 
      (car list)
      (nth2 (1- index) (cdr list))))

(nth2 index test-list)  ; ==> 6 

典型的循环版本

(defun nth3 (index list)
  (loop :for e :in list
        :for i :from index :downto 0
        :when (= i 0) :return e))

(nth3 index test-list)  ; ==> 6 

通常情况下,macro是您在看到自己重复太多而无法使用函数进一步抽象代码时使用的东西。您可以创建一个宏来节省编写样板代码的时间。当然,有一个不是标准代码的权衡,所以你经常在编写样板后写宏。

除非你真的需要,否则永远不要使用

eval。通常,您可以使用funcallapplyeval仅适用于全局范围,因此您会松开闭包变量。