Заявления не выполняются по порядку? (defvar внутри оператора let) - PullRequest
2 голосов
/ 03 июля 2011

Я пытался свести его к минимальному примеру. Код выполняется без ошибок, производя ожидаемый результат. Но это дает мне предупреждение, что моя первая переменная не определена. Похоже, что второе утверждение progn не «видит» результаты первого утверждения. Спасибо за помощь!

(Изначально у меня вообще не было конструкции progn в коде, но после получения этой ошибки я добавил ее, чтобы посмотреть, приведет ли это к принудительному выполнению - но ошибка та же.)

Вот код:

(let ((input (open "input.lisp")))
  (progn (defvar var1 (read input))
         (defvar arr1 (make-array var1 :initial-contents (read input))))
  (close input))

(print var1)
(print arr1)

Это содержимое файла "input.lisp":

9
(10 8 6 4 2 4 6 8 10)

И это вывод, который я получаю из sbcl после выполнения (загрузить "test.lisp"):

; in: DEFVAR ARR1
;     (MAKE-ARRAY VAR1 :INITIAL-CONTENTS (READ INPUT))
; 
; caught WARNING:
;   undefined variable: VAR1
; 
; compilation unit finished
;   Undefined variable:
;     VAR1
;   caught 1 WARNING condition

9 
#(10 8 6 4 2 4 6 8 10) 
T

Итак, мне кажется, что оба определения определения выполняются, но второе не «видит» результаты первого. Он все еще правильно строит массив, потому что он заполнен заданным начальным содержимым. Но почему еще не определено var1?

1 Ответ

4 голосов
/ 04 июля 2011

См. Документацию для defvar в Hyperspec:

Если форма defvar или defparameter отображается как форма верхнего уровня, компилятор должен распознать, что имя было объявлено special.

Это подразумевает (и это похоже на случай с SBCL), что если defvar появляется как форма не верхнего уровня, то компилятору не нужно распознавать, что имя было объявлено. Так почему же ваши defvar не компилируются как формы верхнего уровня? См. раздел 3.2.3.1, Обработка форм верхнего уровня (пункт 6) для ответа: let, окружающий ваш код, вызывает его компиляцию как формы не верхнего уровня.

Так что вам нужно defvar ваши переменные на верхнем уровне, а затем назначить их позже с setf внутри let.


Вот так. Также обычно проще использовать with-open-file вместо open и close.

(defvar var1)
(defvar arr1)

(with-open-file (input "input.lisp" :direction :input)
  (setf var1 (read input))
  (setf arr1 (make-array var1 :initial-contents (read input))))

(print var1)
(print arr1)

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

Например, это был бы более типичный способ написания такого рода кода с кодом инициализации в его собственной функции.

(defvar *var1* nil "Documentation for var1.")
(defvar *arr1* nil "Documentation for arr1.")

(defun init-from-file (file)
  "Read *var1* and *arr1* from file."
  (with-open-file (input file :direction :input)
    (setf *var1* (read input))
    (setf *arr1* (make-array *var1* :initial-contents (read input)))))

(when (null *var1*) (init-from-file "input.lisp"))
...