我在Common Lisp中有一个泛型实现的合并排序:我有不同的拆分和合并函数实现,并且对于拆分和合并函数的每个组合,我想构建一个合并排序函数。
通过调用以下函数创建每个合并排序函数:
(defun make-merge-sort-function (split-function merge-function)
(defun merge-sort (lst)
(if (or (null lst) (null (cdr lst)))
lst
(let ((s (funcall split-function lst)))
(funcall merge-function (merge-sort (car s)) (merge-sort (cadr s))))))
(lambda (lst)
(merge-sort lst)))
我在merge-sort
中定义了make-merge-sort-function
,以便将其名称保密,避免破坏名称空间。
我的代码适用于几个Common Lisp实现(例如Steel Bank Common Lisp):
所以,我认为我的代码是正确的。
但是,如果我使用Allegro Common Lisp运行该程序,我会收到警告:
Warning: MERGE-SORT is defined more than once as `operator' in file foo.lisp.
其中foo.lisp
是调用make-merge-sort-function
的文件。因此,程序运行正常,但是在第一个调用后make-merge-sort-function
每次调用时都会打印一次此警告。
如果我将merge-sort
函数设为全局(另外两个参数split-function
和merge-function
),那么警告就会消失。
我在Allegro Common Lisp中没有发现任何有关此警告含义的迹象。我尝试的其他实现(ABCL,CMUCL,CCL,CLISP,SBCL)不会发出任何警告。我认为内部函数(闭包)的新实例被多次定义是可以的,我不明白为什么这应该是一个问题。 有什么想法吗?
答案 0 :(得分:4)
你永远在Common Lisp中嵌套defun
。
就像你在里面使用let
一样
defun
代替
你使用defvar
而是labels
嵌套defun
:
(defun make-merge-sort-function (split-function merge-function)
(labels ((merge-sort (lst)
(if (or (null lst) (null (cdr lst)))
lst
(let ((s (funcall split-function lst)))
(funcall merge-function (merge-sort (car s))
(merge-sort (cadr s)))))))
#'merge-sort))
答案 1 :(得分:1)
您没有显示测试代码,因此,请考虑此CLISP会话记录:
[3]> (defun make-merge-sort-function (split-function merge-function)
(defun merge-sort (lst)
(if (or (null lst) (null (cdr lst)))
lst
(let ((s (funcall split-function lst)))
(funcall merge-function (merge-sort (car s))
(merge-sort (cadr s))))))
(lambda (lst)
(merge-sort lst)))
MAKE-MERGE-SORT-FUNCTION
[4]> (fboundp 'merge-sort)
NIL <<---------------------------- NB
[5]> (defun sp1(x)(princ" IN SPL1 ") x)
SP1
[6]> (defun sp2(x)(princ" IN SPL2 ") x)
SP2
[7]> (defun mg1(x y)(princ" IN MRG1 ") x)
MG1
[9]> (setq f1 (make-merge-sort-function #'sp1 #'mg1))
#<FUNCTION :LAMBDA (LST) (MERGE-SORT LST)>
[10]> (fboundp 'merge-sort)
T <<---------------------------- NB !!
[12]> (funcall f1 '(1 2 3))
IN SPL1 <<---------------------------- NB
*** - CDR: 1 is not a list
[14]> (setq f2 (make-merge-sort-function #'sp2 #'mg1))
#<FUNCTION :LAMBDA (LST) (MERGE-SORT LST)>
[15]> (funcall f1 '(1 2 3))
IN SPL2 <<---------------------------- NB !!!
*** - CDR: 1 is not a list
现在你可以看到你的代码正在做一些与你想象的不同的事情,你的测试代码可能只是没有把这个问题搞清楚。
显然,嵌套的defun
定义了一个全局可访问的函数merge-sort
,第二次调用make-merge-sort-function
重新定义了它。