Spec-Zone.ru › Elisp

Локальные переменные

Глобальные переменные сохраняют свои значения до явного присваивания нового. Иногда полезно присвоить переменной локальное значение — значение, действующее только в определенной части программы Lisp. Когда переменной присвоено локальное значение, говорят, что она связана локально с этим значением и является локальной переменной.

Например, когда вызывается функция, ее аргументные переменные получают локальные значения, которые являются фактическими аргументами, переданными при вызове функции; эти локальные связи действуют в теле функции. В качестве другого примера, специальный оператор let явно устанавливает локальные связи для определенных переменных, которые действуют только в теле оператора let.

Также говорят о глобальной связи, где (концептуально) хранится глобальное значение.

Установка локальной связи сохраняет предыдущее значение переменной (или его отсутствие). Говорят, что предыдущее значение затеняется. Как глобальные, так и локальные значения могут быть затенены. Если действует локальная связь, использование setq для локальной переменной сохранит указанное значение в локальной связи. Когда эта локальная связь больше не действует, возвращается ранее затененное значение (или его отсутствие).

Переменная может иметь более одной локальной связи одновременно (например, если есть вложенные операторы let которые связывают переменную). Текущая связь — это локальная связь, которая действует фактически. Она определяет значение, возвращаемое при вычислении символа переменной, и она обрабатывается оператором setq.

Для большинства целей вы можете считать текущую связь самой внутренней локальной связью или глобальной связью, если локальная связь отсутствует. Более точно, правило, называемое правилом области видимости, определяет, где в программе локальная связь действует. Умолчание по области видимости в Emacs Lisp — динамическая область видимости, которая просто утверждает, что текущая связь в любой момент выполнения программы — это последняя созданная связь для данной переменной, которая всё ещё существует. Подробности о динамической области видимости и альтернативном правиле области видимости, называемом лексической областью видимости, см. в Области видимости переменных.

Для создания локальных связей существуют специальные операторы let и let*:

Специальный оператор: let (связи…) формы…

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

Каждая из связей представляет собой либо (i) символ, в этом случае символ связывается локально со значением nil; либо (ii) список вида (symbol value-form), в этом случае символ связывается локально с результатом вычисления значение-форма. Если значение-форма опущена, используется nil.

Все значение-формы в связях вычисляются в порядке их появления и до привязки любых символов к ним. Вот пример этого: z присваивается старому значению y, которое равно 2, а не новому значению y, которое равно 1.

(setq y 2)
     ⇒ 2
(let ((y 1)
      (z y))
  (list y z))
     ⇒ (1 2)

С другой стороны, порядок связей не определён: в следующем примере может быть напечатано либо 1, либо 2.

(let ((x 1)
      (x 2))
  (print x))

Поэтому избегайте связывания одной и той же переменной более одного раза в одной форме let.

Специальный оператор: let* (связи…) формы…

Этот специальный оператор похож на let, но он связывает каждую переменную сразу после вычисления её локального значения, прежде чем вычислить локальное значение для следующей переменной. Следовательно, выражение в связях может ссылаться на предыдущие символы, связанные в данной форме let*. Сравните следующий пример с примером выше для let.

(setq y 2)
     ⇒ 2
(let* ((y 1)
       (z y))    ; Use the just-established value of y.
  (list y z))
     ⇒ (1 1)
Специальный оператор: letrec (связи…) формы…

Этот специальный оператор похож на let*, но все переменные связаны до вычисления любых локальных значений. Затем значения присваиваются связанным локально переменным. Это полезно только при лексической области видимости, и вы хотите создать замыкания, которые ссылаются на связи, которые в противном случае ещё не действовали бы при использовании let*.

Например, вот замыкание, которое удаляет себя из обработчика после выполнения один раз:

(letrec ((hookfun (lambda ()
                    (message "Run once")
                    (remove-hook 'post-command-hook hookfun))))
  (add-hook 'post-command-hook hookfun))
Специальный оператор: dlet (связи…) формы…

Этот специальный оператор похож на let, но он связывает все переменные динамически. Это редко полезно — вы обычно хотите связать обычные переменные лексически, а специальные переменные (т.е. переменные, определённые с помощью defvar) динамически, и именно это делает let.

dlet может быть полезно при взаимодействии со старым кодом, предполагающим, что определённые переменные связаны динамически (см. Динамическая область видимости), но практично defvar эти переменные. dlet временно сделает связанные переменные специальными, выполнит формы, а затем снова сделает переменные неспециальными.

Специальный оператор: named-let имя связи &rest тело

Этот специальный оператор — циклическая конструкция, вдохновлённая языком Scheme. Он похож на let: Он связывает переменные в связях, а затем вычисляет тело. Однако, named-let также связывает имя с локальной функцией, формальные аргументы которой — переменные в связях, а тело — тело. Это позволяет телу вызывать себя рекурсивно, вызывая имя, где аргументы, передаваемые в имя, используются в качестве новых значений связанных переменных в рекурсивном вызове.

Пример цикла, суммирующего список чисел:

(named-let sum ((numbers '(1 2 3 4))
                (running-sum 0))
  (if numbers
      (sum (cdr numbers) (+ running-sum (car numbers)))
    running-sum))
⇒ 10

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

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

Вот полный список других средств, которые создают локальные связи:

  • Вызовы функций (см. Функции).
  • Вызовы макросов (см. Макросы).
  • condition-case (см. Ошибки).

Переменные также могут иметь локальные в буфере связи (см. Переменные локальные для буфера); несколько переменных имеют локальные для терминала связи (см. Несколько терминалов). Эти типы связей работают примерно как обычные локальные связи, но они локализуются в зависимости от того, где вы находитесь в Emacs.

Параметр пользователя: max-specpdl-size

Эта переменная определяет предел общего количества локальных связей переменных и unwind-protect очисток (см. Очистка после выхода из нелокальной области), разрешённых до того, как Emacs сигнализирует об ошибке (с данными "Variable binding depth exceeds max-specpdl-size").

Этот предел с соответствующей ошибкой при его превышении — один из способов, которым Lisp избегает бесконечной рекурсии в некорректно определённой функции. max-lisp-eval-depth предоставляет другой предел глубины вложенности. См. Оценивание.

Значение по умолчанию — 1600. Вход в отладчик Lisp увеличивает значение, если места мало, чтобы убедиться, что сам отладчик имеет место для выполнения.

Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Local-Variables.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API