如何在Lisp中的&REST参数上编写递归宏调用?

Wil*_*hin 6 lisp macros recursion common-lisp

我一直在为我的一个作业编写一些简单的测试用例,并使用宏构建了一些测试套件.我有run-testrun-test-section等.我想run-test-section采取一些参数作为run-test调用,并计算PASS和FAIL的数量.

run-test 在PASS上返回T,在FAIL上返回NIL.

我现在要做的是编写一个接受&REST参数的宏,并调用该列表的每个元素,最后返回TRUE值的数量.

这就是我目前拥有的:

(defmacro count-true (&rest forms)
`(cond
    ((null ,forms)
      0)
    ((car ,forms)
      (1+ (count-true (cdr ,forms))))
    (T
      (count-true (cdr ,forms)))))
Run Code Online (Sandbox Code Playgroud)

然而,这使我的REPL陷入无限循环.有人可能会指出我如何能更有效地操纵这些论点.这甚至是个好主意吗?有更好的方法吗?

编辑:

如在响应中所指出的,在这种情况下不需要宏.使用内置COUNT就足够了.但是,在递归宏调用的响应中有有用的信息.

Nat*_*ers 5

在宏扩展时,cdr不进行评估.所以(count-true t t nil)点击这样的无限扩展:

(count-true t t nil)
=>
(1+ (count-true (cdr (t t t nil))))
=>
(1+ (1+ (count-true (cdr (cdr (t t t nil))))))
=>
(1+ (1+ (1+ (count-true (cdr (cdr (cdr (t t t nil))))))))
=> ...
Run Code Online (Sandbox Code Playgroud)

嗯,实际上这同时发生在两个递归分支上.所以它比例子更快爆炸.

一个更好的主意?

  1. 首先尝试编写与函数相同的东西.找出你必须将lambda用于延迟评估的位置.然后将函数抽象为宏,这样就可以省略lambdas.

    顺便说一句,首先编写函数的一点是,有时你会发现函数足够好.编写一个函数可以执行的宏是一个错误.

  2. 通常,当您在Common Lisp中编写宏时,请先使用loop而不是递归.递归宏很棘手(通常是错误的:).

编辑:

这是一个更正确(但更长)的例子:

(count-true t nil) =>
(cond
  ((null '(t nil)) 0)
  ((car '(t nil)) (1+ (count-true (cdr '(t nil)))))
  (T (count-true (cdr '(t nil)))))
=>
(cond
  ((null '(t nil)) 0)
  ((car '(t nil)) (1+ (1+ (count-true (cdr (cdr '(t nil)))))))
  (T (count-true (cdr (cdr '(t nil))))))
=>
(cond
  ((null '(t nil)) 0)
  ((car '(t nil)) (1+ (1+ (1+ (count-true (cdr (cdr (cdr '(t nil)))))))))
  (T (count-true (cdr (cdr (cdr '(t nil)))))))
=>
(cond
  ((null '(t nil)) 0)
  ((car '(t nil)) (1+ (1+ (1+ (1+ (count-true (cdr (cdr (cdr (cdr '(t nil)))))))))))
  (T (count-true (cdr (cdr (cdr (cdr '(t nil))))))))
Run Code Online (Sandbox Code Playgroud)