Вложенный `defun` выдает повторное предупреждение в Allegro Common Lisp
У меня есть общая реализация сортировки слиянием в 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), не выдают никаких предупреждений. Я думаю, что все в порядке, что свежий экземпляр внутренней функции (замыкание) определяется несколько раз, и я не понимаю, почему это должно быть проблемой. Есть идеи?
2 ответа
Вы никогда не гнездитесь defun
в Common Lisp.
Так же, как вы используете 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))
Вы не показываете свой тестовый код, поэтому рассмотрите следующую стенограмму сеанса 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
переопределяет это.