Подтвердить что ты не робот

Разница между LET и SETQ?

Я программирую на Ubuntu, используя GCL. Из документации по Common Lisp из разных источников я понимаю, что let создает локальные переменные, а setq устанавливает значения существующих переменных. В следующих случаях мне нужно создать две переменные и суммировать их значения.

Использование setq

(defun add_using_setq ()
  (setq a 3)  ; a never existed before , but still I'm able to assign value, what is its scope?
  (setq b 4)  ; b never existed before, but still I'm able to assign value, what is its scope?
  (+ a b))

Использование let

(defun add_using_let ( )
  (let ((x 3) (y 4)) ; creating variables x and y
     (+ x y)))

В обоих случаях я, кажется, добился того же результата; в чем разница между использованием setq и let здесь? Почему я не могу использовать setq (так как это синтаксически легко) во всех местах, где мне нужно использовать let?

4b9b3361

Ответ 1

setq присваивает значение переменной, тогда как let вводит новые переменные/привязки. Например, посмотрите, что происходит в

(let ((x 3))
  (print x)      ; a
  (let ((x 89))
    (print x)    ; b
    (setq x 73)  
    (print x))   ; c
  (print x))     ; d


3   ; a
89  ; b
73  ; c
3   ; d

Внешний let создает локальную переменную x, а внутренний let создает другую локальную переменную, затеняющую внутреннюю. Обратите внимание, что использование let для теневой переменной не влияет на значение тени; x в строке d есть x, введенный внешним let, и его значение не изменилось. setq влияет только на переменную, с которой она вызывается. В этом примере показано, что setq используется с локальными переменными, но также может быть со специальными переменными (что означает, динамически ограничено и обычно определяется с помощью defparameter или defvar:

CL-USER> (defparameter *foo* 34)
*FOO*
CL-USER> (setq *foo* 93)
93
CL-USER> *foo*
93

Обратите внимание, что setq не создает (переносимо) создание переменных, тогда как let, defvar, defparameter, & c. делать. Поведение setq при вызове с аргументом, который не является переменной (пока), не определен, и это зависит от реализации, чтобы решить, что делать. Например, SBCL громко жалуется:

CL-USER> (setq new-x 89)

; in: SETQ NEW-X
;     (SETQ NEW-X 89)
; 
; caught WARNING:
;   undefined variable: NEW-X
; 
; compilation unit finished
;   Undefined variable:
;     NEW-X
;   caught 1 WARNING condition
89

Конечно, лучшие способы лучше понять эти понятия - читать и писать больше Lisp кода (который приходит со временем) и читать записи в HyperSpec и следовать перекрестным ссылкам, особенно глоссарий записей. Например, краткие описания из HyperSpec для setq и let включают:

  • setq

    Назначает значения переменные.

  • let

    let и let * создайте новую переменную привязки и выполните серию форм, которые используют эти привязки.

Вы можете больше узнать о переменных и привязках. let и let* также имеют особое поведение с динамическими переменными и объявлениями special (но вам, вероятно, не понадобится некоторое время об этом знать), а в некоторых случаях (вам, вероятно, не понадобится знать некоторое время), когда переменная на самом деле не является переменной, setq фактически эквивалентна setf. HyperSpec имеет более подробную информацию.

Есть несколько не совсем повторяющихся вопросов о переполнении стека, которые могут, тем не менее, помочь в понимании использования различных операторов определения и назначения переменных, доступных в Common Lisp:

Ответ 2

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

Мне нравится описание в руководстве emacs lisp:

let используется для привязки или привязки символа к значению таким образом, что интерпретатор lisp не будет путать переменную с переменной с тем же именем, которая не является частью функции.

Чтобы понять, почему нужна специальная форма, рассмотрите ситуацию, в которой вы владеете домом, который вы обычно называете the house', as in the sentence, "The house needs painting." If you are visiting a friend and your host refers to домом ", он, вероятно, будет ссылаться на свой дом, а не ваш, то есть, в другой дом.

Если ваш друг ссылается на свой дом, и вы думаете, что он имеет в виду ваш дом, вы можете быть в замешательстве. То же самое может произойти в lisp, если переменная, которая используется внутри одной функции, имеет то же имя, что и переменная, которая используется внутри другой функции, и эти два не предназначены для обозначения одного и того же значения. Специальная форма let предотвращает такое замешательство.

- http://www.gnu.org/software/emacs/manual/html_node/eintr/let.html

Ответ 3

(setq x y) назначает новое значение y переменной, обозначенной символом x, опционально определяя новую переменную уровня пакета 1. Это означает, что после того, как вы вызвали add_using_setq, в текущем пакете у вас будет две новые переменные уровня пакета.

(add_using_setq)
(format t "~&~s, ~s" a b)

напечатает 3 4 - маловероятный желаемый результат.

В отличие от этого, когда вы используете let, вы назначаете новые значения только переменным, обозначенным символами, для продолжительности функции, поэтому этот код приведет к ошибке:

(add_using_let)
(format t "~&~s, ~s" a b)

Подумайте о let как эквиваленте следующего кода:

(defun add-using-lambda ()
  (funcall (lambda (a b) (+ a b)) 3 4))

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

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

Ответ 4

  • SETQ

вы можете получить значение символа вне области действия, пока Lisp все еще работает. (он присваивает значение символу)

  • LET

вы не можете получить значение символа, определенного с помощью LET после завершения Lisp оценки формы. (привязывает значение к символу и создает новое привязку к символу)

рассмотрим пример ниже:

;; with setq
CL-USER> (setq a 10)
CL-USER> a
10

;; with let
CL-USER> (let ((b 20))
       (print b))
CL-USER> 20
CL-USER> b ; it will fail
; Evaluation aborted on #<UNBOUND-VARIABLE B {1003AC1563}>.
CL-USER>