Невозможно скомпилировать файл, когда defconstant использует функцию из того же файла

#common-lisp

Вопрос:

Это example.lisp :

 (defun f (x)
  (* x 2))

(defconstant  n  (f 1))
 

Когда я пытаюсь скомпилировать файл с помощью (compile-file "example.lisp") , я получаю сообщение об ошибке при компиляции константы: The function COMMON-LISP-USER::F is undefined . Как это может быть? Я уже определил функцию f , прежде чем использовать ее для определения константы. Как мне определить константу, значение которой является возвращаемым значением функции?

Я использую SBCL 2.0.1.

Ответ №1:

От DEFCONSTANT :

[…] пользователи должны убедиться, что начальное значение может быть вычислено во время компиляции (независимо от того, появляются ли ссылки на name в файле) и что оно всегда принимает одно и то же значение.

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

Вам нужен LOAD результирующий файл, чтобы определение было доступно в вашей среде (например, Slime компилирует файл и загружает результат). Аналогично, когда вы оцениваете формы по одной за раз в REPL, определение становится доступным сразу, как если бы был выполнен небольшой цикл компиляции-загрузки.

У вас есть два основных варианта:

  • оберните defun в eval-when форму
       (eval-when (:compile-toplevel :load-toplevel :execute)
        (defun f (x) (* x 2)))
     
  • поместите две формы в разные файлы, и пусть вторая зависит в системе (в смысле defsystem) от первой:
       (defsystem "foo"
        :components ((:file "define-f")
                     (:file "define-constant"))
        :serial t)
     

Комментарии:

1. Я заметил, что могу избежать проблемы, используя defvar вместо defconstant . Это правильное решение?

2. Да, обычно defconstant вычисляет начальное значение во время компиляции, чтобы значение можно было ввести непосредственно позже в том месте, где на него ссылаются; это не относится к defvar, код ссылается только на переменную (которую вы также можете динамически привязывать). Вот почему начальное значение может быть оценено позже (во время загрузки / оценки). Таким образом, оба подхода не эквивалентны в рабочем состоянии, но это, возможно, не важно в вашем случае