Вложенный `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 переопределяет это.

Другие вопросы по тегам