变量不是 Lisp 中的数字错误(这是不正确的)



我有一个代码,它接受一个列表并通过参数result返回所有可能的排列。 但是当我编译时,我有一个错误,上面写着*** - =: (1+ INDEX) is not a number. 这条消息是真的还是我一般搞砸了代码? 我是 lisp 的新手,我可以寻找修复程序,也愿意接受功能程序员的建议。

;; Creates permutatiions of a given list and returns it via parameter
(defun create-permuations (source)
(setf result (list))
(create-permuations-helper source 0 '() result)
result)
(defmacro create-permuations-helper (source index cur result)
(if (= (list-length cur) index)
(cons cur result)
(loop for i from 0 to (list-length cur) do
(create-permuations-helper source (1+ index)
(append cur (list (nth i source))) result))))

99% 的编译器报告错误时,您可以相信它是真的。这里的索引是列表(1+ index),字面意思是1+符号后跟index符号。之所以如此,是因为您使用的是宏,而宏对代码进行操作。

在宏中,您不返回要评估的表单,而是在依赖于自身的宏扩展期间执行代码。仅此一项就是一种未定义的行为。例如:

(defmacro a (x)
(if (plusp x)
(a (- x 1))
nil))

a的正文中,您希望使用对自身的递归调用扩展代码。但是宏尚不完全已知,直到定义整个宏才能完全知道。

也许特定的 lisp 实现a绑定到宏主体中的宏函数,这是一件奇怪的事情,或者你对定义进行了两次评估。编译器第一次假定a是一个未知函数,然后将a绑定到宏,第二次尝试扩展宏。 无论如何,宏不应该是递归的。

在该示例中,由于宏不计算其参数,因此对宏的嵌套调用被赋予文字表达式(- x 1),而不是它的实际值,无论如何都无法知道,因为x是未知的。您在这里通过尝试在宏观扩展时评估事物来跨越抽象级别。

但是,宏可以扩展到引用自身的代码。

(defmacro a (x)
(if (plusp x)
`(b (a ,(- x 1)))
nil))

现在,(a 2)扩展到(b (a 1)),而本身宏观扩展到(b (b (a 0))),最后到达一个(b (b nil))的定点。

不同之处在于宏生成一段代码并返回,编译器宏再次展开该代码,而在第一个示例中,宏必须已经在其自己的定义正文中展开。

可能的实现

解决问题的一种方法是定义一个局部函数,该函数可以访问主函数中定义的变量。然后,局部函数可以设置它,并且不需要通过引用传递变量(这是不可能的(:

(defun permut (list)
(let (result)
(labels ((recurse (stack list)
(if list
(dolist (x list)
(recurse (cons x stack)
(remove x list :count 1)))
(push stack result))))
(recurse nil list))
result))

或者,您可以将进程一分为二;首先,定义permut-helper,这是一个接受回调函数的高阶函数;它生成排列并为每个排列调用回调:

(defun permut-helper (stack list callback)
(if list
(dolist (x list)
(permut-helper (cons x stack)
(remove x list :count 1)
callback))
(funcall callback stack)))

您可以使用将结果推送到排列列表中的函数调用它:

(defun permut (list)
(let (result)
(flet ((add-result (permutation)
(push permutation result)))
(permut-helper nil list #'add-result))
result))

最新更新