Операторы выполняются не по порядку? (defvar в операторе let)

Я попытался уменьшить его до минимального примера. Код работает без ошибок, выдавая ожидаемый результат. Но это дает мне предупреждение, что моя первая переменная не определена. Кажется, что второй оператор 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 еще не определен?


person usul    schedule 03.07.2011    source источник
comment
Вы всегда должны окружать лексические переменные наушниками. Вы должны называть их var1 и arr1. В противном случае вы столкнетесь с неприятностями. И я не хочу читать твой код.   -  person whoplisp    schedule 04.07.2011
comment
Я не знаю, что вы имеете в виду под наушниками? Я назвал переменные так только потому, что хотел, чтобы этот минимальный рабочий пример был понятен. [... вырезано бла-бла-бла...]. Редактировать: я искал наушники. Спасибо за совет. Проблема, с которой я сталкиваюсь, заключается в том, что либо а) практически все мои переменные должны быть глобальными (лексическими?), либо б) я просто хочу обернуть все в огромные вложенные операторы let. Есть ли хорошая золотая середина?   -  person usul    schedule 04.07.2011
comment
Наушники — это звезды, окружающие имя переменной. Вы, несомненно, сталкивались с ними раньше. Не нужно верить мне на слово, достаточно прочитать мнение настоящего профессионала (Никодимуса): random-state.net/log/3498750808.html.   -  person whoplisp    schedule 04.07.2011
comment
О да, я вижу, что наушники хороши, но если эти переменные такие особенные, мне кажется, что у меня их слишком много... Еще раз спасибо за совет.   -  person usul    schedule 04.07.2011
comment
whoplisp немного сбит с толку. Принято, что специальные переменные (объявленные defvar) получают наушники, а не лексические переменные (объявленные let). Это всего лишь соглашение, так что вам не нужно следовать ему, если оно вам не нравится, но об этом стоит знать, если вам когда-нибудь понадобится читать код, написанный другими программистами на Лиспе.   -  person Gareth Rees    schedule 04.07.2011
comment
Является ли специальная переменная техническим термином? Я правильно понял значение whoplisp, я как бы проанализировал лексическое значение как глобальное.... Но вы определяете специальную переменную как что-либо, объявленное defvar?   -  person usul    schedule 04.07.2011


Ответы (1)


См. документацию по 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"))
person Gareth Rees    schedule 03.07.2011
comment
Хорошо, спасибо, это имеет смысл. Я действительно не получил формы высшего уровня, но я думаю, что понимаю.... - person usul; 04.07.2011
comment
Означает ли это, что я должен написать (defvar input (open input.lisp)) так как я не могу использовать конструкцию let? - person usul; 04.07.2011
comment
См. код. Вы можете использовать let, если хотите, но в этом случае лучше использовать with-open-file. - person Gareth Rees; 04.07.2011
comment
Спасибо. Я хотел бы использовать макрос для определения и каждой переменной в соответствии с вводом, поэтому defvars были в операторе let в первую очередь. Любые советы по этому поводу? Должен ли я сначала определить переменные, а макрос просто установить каждую из них? - person usul; 04.07.2011
comment
Это был бы один подход, или (в зависимости от того, что именно вы делаете) вы могли бы заставить макрос генерировать все это: defvar, setf и все. - person Gareth Rees; 04.07.2011