是否可以在lisp中将递归函数重写为宏?

时间:2010-01-20 20:23:47

标签: recursion lisp common-lisp

我写了这个快速排序功能:

(defun quicksort (lst)
  (if (null lst)
      nil
      (let ((div  (car lst))
            (tail (cdr lst)))
        (append (quicksort (remove-if-not (lambda (x) (< x div)) tail))
                (list div)
                (quicksort (remove-if     (lambda (x) (< x div)) tail))))))

但我无法将其重写为宏,它不起作用,也不会,例如,这个简单的foo(递归总和 - 我知道,有点傻,但仅作为示例):

(defun Suma (lst)
  (if (cdr lst) 
      (+ (Suma (cdr lst))
         (car lst))
      (car lst)))

正常,但宏:

(defmacro SumaMacro (lst)
  '(if (cdr lst)
       '(+ (prog (SUMAMACRO (cdr lst)))
           (prog (car lst)))
       '(car lst)))

似乎是错的。有人有任何关于将递归函数重写为宏的建议吗?

2 个答案:

答案 0 :(得分:4)

将SUM或QUICKSORT等递归函数编写为宏是没有意义的。而且,不,通常不可能。宏扩展了源代码。在编译时,宏只看到源代码,但不看到调用代码的真实参数。编译完成后,宏将消失并替换为它生成的代码。然后使用参数调用此代码。因此,宏不能在编译时根据仅在运行时知道的参数值进行计算。

例外是:当在编译时/宏扩展时知道参数值时,宏可以扩展为对自身的递归宏调用。但这确实是高级宏用法,并且没有任何人可以添加到其他程序员维护的代码中。

经验法则:如果要进行递归计算,则使用函数。如果要处理源代码,请使用宏。

另外,尝试使用类似Lisp的格式。编辑器计算括号,突出显示和缩进。不要把括号放在自己的线上,他们在那里感到孤独。通常的Lisp风格更紧凑,也更多地使用水平空间。如果使用列表,则使用FIRST和REST,而不是CAR和CDR。

你的'suma'功能看起来像这样:

(defun suma (list) 
  (if (rest list)
      (+ (suma (rest list))
         (first list))
    (first list)))

忘了宏。但是,如果您想了解有关宏的更多信息,那么Paul Graham的书籍{On Lisp'(可下载)是一个很好的知识来源。

答案 1 :(得分:4)

你正在混合宏和运行时;换句话说,你正在混合价值观和语法。这是一个非常简单的例子:

(defmacro while (condition &body body)
  `(when ,condition ,@body (while ,condition ,@body)))

这里的坏处是宏不会执行主体,它只是在其中构造一段具有给定主体的代码。因此,当函数中存在这种循环时,它受某些条件保护,如if,这将阻止无限循环。但是在这个宏代码中没有这样的条件 - 你可以看到宏扩展为精确的原始形式,这意味着它正在尝试扩展到一些无限的代码。这就像你写的那样

(defun foo (blah)
  (cons 1 (foo blah)))

然后将生成器函数挂钩到编译器中。所以要做这些类型的运行时循环,你必须使用一个真正的函数。 (当需要时,您可以使用labels创建一个本地函数来执行递归工作。)