setq и defvar в Лиспе

Я вижу, что Практический Common Lisp использует (defvar *db* nil) для настройки глобальной переменной. Разве это не нормально использовать setq для той же цели?

Каковы преимущества / недостатки использования defvar против setq?

5 ответов

Решение

Есть несколько способов введения переменных.

DEFVAR и DEFPARAMETER вводят глобальные динамические переменные. DEFVAR опционально устанавливает его в какое-то значение, если оно уже не определено. DEFPARAMETER устанавливает его всегда в соответствии с предоставленным значением. SETQ не вводит переменную.

(defparameter *number-of-processes* 10)

(defvar *world* (make-world))     ; the world is made only once.

Обратите внимание, что вы, вероятно, никогда не захотите DEFVAR переменные с такими именами, как x, y, stream, limit,... Зачем? Потому что тогда эти переменные будут объявлены специальными, и это трудно отменить. Специальное объявление является глобальным, и при дальнейшем использовании переменной будет использоваться динамическое связывание.

ПЛОХОЙ:

(defvar x 10)     ; global special variable X, naming convention violated
(defvar y 20)     ; global special variable Y, naming convention violated

(defun foo ()
  (+ x y))        ; refers to special variables X and y

(defun bar (x y)  ; OOPS!! X and Y are special variables
                  ; even though they are parameters of a function!
  (+ (foo) x y))

(bar 5 7)         ; ->   24

ЛУЧШЕ: Всегда помечайте специальные переменные * в их именах!

(defvar *x* 10)     ; global special variable *X*
(defvar *y* 20)     ; global special variable *Y*

(defun foo ()
  (+ *x* *y*))      ; refers to special variables X and y

(defun bar (x y)    ; Yep! X and Y are lexical variables
  (+ (foo) x y))

(bar 5 7)           ;  ->   42

Локальные переменные вводятся с помощью DEFUN, LAMBDA, LET, MULTIPLE-VALUE-BIND и многих других.

(defun foo (i-am-a-local-variable)
   (print i-am-a-local-variable))

(let ((i-am-also-a-local-variable 'hehe))
  (print i-am-also-a-local-variable))

Теперь по умолчанию локальные переменные в двух вышеуказанных формах являются лексическими, если они не объявлены как СПЕЦИАЛЬНЫЕ. Тогда они будут динамическими переменными.

Далее, есть также несколько форм для установки переменной в новые значения. SET, SETQ, SETF и другие. SETQ а также SETF Можно установить как лексические, так и специальные (динамические) переменные.

Для переносимого кода требуется, чтобы переменные уже были объявлены. Точный эффект установки не объявленной переменной не определен стандартом.

Итак, если вы знаете, что делает ваша реализация Common Lisp, вы можете использовать

(setq world (make-new-world))

в цикле чтения-оценки-печати на верхнем уровне. Но не используйте его в своем коде, так как эффект не переносимый. типично SETQ установит переменную. Но некоторая реализация может также объявить переменную SPECIAL, когда она не знает об этом (по умолчанию CMU Common Lisp делает это). Это почти всегда не то, что хотелось бы. Используйте его для повседневного использования, если вы знаете, что делаете, но не для кода.

Тоже самое:

(defun make-shiny-new-world ()
  (setq world (make-world 'shiny)))

Во-первых, такие переменные должны быть записаны как *world* (с окружающими * символов), чтобы было ясно, что это глобальная специальная переменная. Во-вторых, это должно было быть объявлено с DEFVAR или же DEFPARAMETER до.

Типичный компилятор Lisp будет жаловаться, что указанная выше переменная не объявлена. Поскольку глобальные лексические переменные не существуют в Common Lisp, компилятор должен генерировать код для динамического поиска. Затем какой-то компилятор скажет: "Хорошо, мы предполагаем, что это динамический поиск, давайте объявим его особенным - так как это то, что мы предполагаем в любом случае".

defvar вводит динамическую переменную, а setq используется для присвоения значения динамической или лексической переменной. Значение динамической переменной ищется в среде, которая вызывает функцию, в то время как значение лексической переменной ищется в среде, где была определена функция. Следующий пример прояснит разницу:

;; dynamic variable sample
> (defvar *x* 100)
*X*
> (defun fx () *x*)
FX
> (fx)
100
> (let ((*x* 500)) (fx)) ;; gets the value of *x* from the dynamic scope.
500
> (fx) ;; *x* now refers to the global binding.
100

;; example of using a lexical variable
> (let ((y 200))
   (let ((fy (lambda () (format t "~a~%" y))))
     (funcall fy) ;; => 200
     (let ((y 500))
       (funcall fy) ;; => 200, the value of lexically bound y
       (setq y 500) ;; => y in the current environment is modified
       (funcall fy)) ;; => 200, the value of lexically bound y, which was 
                     ;; unaffected by setq
     (setq y 500) => ;; value of the original y is modified.
     (funcall fy))) ;; => 500, the new value of y in fy's defining environment.

Динамические переменные полезны для передачи значения по умолчанию. Например, мы можем связать динамическую переменную *out* на стандартный вывод, так что он становится выводом по умолчанию для всех функций ввода-вывода. Чтобы переопределить это поведение, мы просто вводим локальную привязку:

> (defun my-print (s)
        (format *out* "~a~%" s))
MY-PRINT
> (my-print "hello")
hello
> (let ((*out* some-stream))
    (my-print " cruel ")) ;; goes to some-stream
> (my-print " world.")
world

Обычно лексические переменные используются при определении замыканий для эмуляции объектов с состоянием. В первом примере переменная y в обязательной среде fy эффективно стал частным государством этой функции.

defvar присвоит значение переменной, только если оно еще не назначено. Таким образом, следующее переопределение *x* не изменит оригинальную привязку:

> (defvar *x* 400)
*X*
> *x*
100

Мы можем назначить новое значение *x* используя setq:

> (setq *x* 400)
400
> *x*
400
> (fx)
400
> (let ((*x* 500)) (fx)) ;; setq changed the binding of *x*, but 
                         ;; its dynamic property still remains.
500
> (fx)
400

DEFVAR устанавливает новую переменную. SETQ присваивает переменной.

Большинство реализаций Лиспа, которые я использовал, выдают предупреждение, если вы SETQ для переменной, которая еще не существует.

defvar а также defparameter оба вводят глобальные переменные. Как отмечает Кен, setq присваивает переменной.

К тому же, defvar не будет забивать что-то ранее defvar-ed. Позже в книге (глава 6) Сейбел говорит: "На практике вы должны использовать DEFVAR для определения переменных, которые будут содержать данные, которые вы хотите сохранить, даже если вы внесли изменения в исходный код, который использует переменную".

http://www.gigamonkeys.com/book/variables.html

Например, если у вас есть глобальный *db* для базы данных в главе "Простая база данных":

(defvar *db* nil)

... и вы начинаете играть с ним в REPL - добавление, удаление вещей и т. д. - но затем вы вносите изменения в исходный файл, который содержит эту форму defvar, перезагрузка этого файла не уничтожит *db* и все изменения, которые вы могли бы сделать... Я считаю, что setq будет, как будет defparameter, Более опытный Лиспер, пожалуйста, поправьте меня, если я ошибаюсь.

Ответ здесь. Очень ясно.

https://www.tutorialspoint.com/lisp/lisp_variables.htm

Глобальные переменные обычно объявляются с использованием defvar построить.

Поскольку в LISP нет объявления типа для переменных, вы напрямую указываете значение для символа с помощью конструкции.

Как и глобальные переменные, локальные переменные также могут быть созданы с помощью setq построить.

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