Круговая зависимость между функциями в Common Lisp - PullRequest
1 голос
/ 21 апреля 2020

Можно ли определить две функции, вызывающие друг друга в Common LISP, без получения предупреждения о стиле? Моя лучшая идея для решения этой проблемы - дать одной из этих функций в качестве аргумента вторую, но я не считаю эту идею очень элегантной.

Ответы [ 4 ]

4 голосов
/ 21 апреля 2020

Common Lisp определяет единицы компиляции , и вы даже можете вручную указать область действия единицы компиляции, используя WITH-COMPILATION-UNIT.

Например, когда вы вызываете COMPILE-FILE, весь файл представляет собой единицу компиляции: компиляторы обычно откладывают предупреждения в конце файла, что означает, что если вы правильно определите две взаимно рекурсивные функции , предупреждения не будет.

Другая возможность - выполнить предварительное объявление:

(declaim (ftype function f))
(defun g () (f))
(defun f () (g))

Первый declaim означает, что f следует считать fbound (символ связан в пространстве имен функции), подавляя предупреждения в момент использования во время компиляции.

4 голосов
/ 21 апреля 2020

Да, конечно, и не требует каких-либо особых маги c.

Просто определите одно, а затем другое. Если вы сделаете это в REPL, или выборочно оценив отдельные определения из файла, или загрузив источник файла, то вы можете получить предупреждение при определении первой функции, пока не будет определена вторая функция (так что вы ' Я получу это только в первый раз, когда вы определите первую функцию):

cl-user> (defun foo (x)
           (if (null x)
               1
               (bar (rest x))))
; in: defun foo
;     (BAR (REST X))
; 
; caught style-warning:
;   undefined function: common-lisp-user::bar
; 
; compilation unit finished
;   Undefined function:
;     bar
;   caught 1 STYLE-WARNING condition
foo
cl-user> (defun bar (x)
           (foo x))
bar
cl-user> (foo '(1 2 3))
1
cl-user>

На практике вы, вероятно, будете получать подобные предупреждения только в реализациях только для компилятора, таких как SBCL и CMUCL. Нет причин, по которым другие реализации также не должны предупреждать: они обычно этого не делают, потому что такие предупреждения обычно раздражают. Их просто нелегко избежать в реализациях только для компилятора, если вы хотите видеть предупреждения, когда действительно является отсутствующим определением.

Если вы просто поместите две функции в файл и скомпилируйте их:

cl-user> (compile-file "/tmp/x.lisp" :load t)
; Evaluation aborted on #<unknown-keyword-argument {100219F653}>.
cl-user> (load (compile-file "/tmp/x.lisp"))
; compiling file "/tmp/x.lisp" (written 21 APR 2020 04:35:27 PM):
; compiling (defun foo ...)
; compiling (defun bar ...)

; wrote /tmp/x.fasl
; compilation finished in 0:00:00.002
t
cl-user> (foo '(1 2 3 4))
1

Вы не получите никаких предупреждений.

В частности, в SBCL (и, вероятно, CMUCL) вы можете предотвратить предупреждения при загрузке исходных файлов, используя with-compilation-unit:

cl-user> (with-compilation-unit ()
           (load "/tmp/x.lisp"))
t

Это откладывает предупреждения от компилятора до конца формы, после чего все они отсутствуют. Я не думаю, что на это можно положиться (я думаю, что это безопасно для других реализаций, но они все равно могут предупредить).

(Обратите внимание, что каждый приведенный выше отрывок взят из fre sh SBCL.)

1 голос
/ 21 апреля 2020

Вы можете определить две взаимно рекурсивные функции без предупреждения стиля, немного запутанно, учитывая тот факт, что defun не требуется для отображения на верхнем уровне (см. Common Lisp the Language, 2nd Edition * * 1003) *):

X3J13 проголосовал в марте 1989 года (DEFINING-MACROS-NON-TOP-LEVEL), чтобы уточнить, что, хотя определения форм обычно появляются на верхнем уровне, целесообразно поместить их в не контексты верхнего уровня; defun должен определять функцию в окружающей лексической среде, а не в нулевой лексической среде.

Примером является использование специального оператора labels ( manual ), что позволяет определять взаимно рекурсивные функции:

CL-USER> (labels ((f (x)
                    (cond ((> x 0) (g (1- x)))
                          ((= x 0) x)
                          (t (g (1+ x)))))
                  (g (y)
                    (if (= y 0)
                        0
                        (f y))))
           (defun f(x) (funcall #'f x))
           (defun g(x) (funcall #'g x)))
G
CL-USER> (f 3)
0
CL-USER> (g 3)
0
0 голосов
/ 21 апреля 2020

Это, конечно, возможно. Вы просто должны быть осторожны, чтобы не создавать бесконечное число oop. То же самое относится и к рекурсии (вам нужен случай завершения).

Кстати, SBCL предупреждает, когда функция вызывает неопределенную функцию.

pozdrowienia!

...