Назначение локальной / глобальной переменной LISP

если мы определяем функцию что-то вроде

(defun foo(x)
  (setf x somevalue))

и x определена как локальная переменная или глобальная? использование setf / q устанавливает глобальное значение. если он глобальный, может ли кто-нибудь сказать мне, как определить локальную переменную в lisp, кроме let?

спасибо!

рассмотрим следующий пример

(let ((x 0)) 
  (defun foo (y) 
    (when (equal x 0) (setq x y)) 
    (when (< x y) (setq x y))
    x))

когда я даю ответ:foo как (foo 2), он возвращает 2, и если мы снова выполним функцию с (foo 1) он по-прежнему возвращает 2 и (foo 3) возвращает 3.Именно этого я и хочу.Но вопрос как это возможно, потому что если я пытаюсь получить доступ к переменной x вне функции от терминала clisp я не могу.Если я снова обращаюсь к функции, она, похоже, сохраняет Предыдущее значение x.

спасибо!

3 ответов


чтобы провести параллель с другими языками, такими как C, C++, Java или Python, что ваш код изменяет "локальную переменную", даже если это не формулировка, которую будет использовать Lisper (формулировка на языке Lisp будет локальной "привязкой").

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

  • (let ((x 12)) ...)
  • (do ((x 0 (1+ i))) ...)
  • (dotimes (x 10) ...)
  • (loop for x from 0 to 10 do ...)

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

(let ((x 10)) ...)

эквивалентно

(funcall (lambda (x) ...) 10)

обратите внимание также, что действительно чтение фрагмента кода возможно, что x в некотором смысле "глобальная переменная", потому что она могла быть объявлена специальной:

(defvar x 12)
;; ==> x

(defun bar ()
  (format t "The value of x is ~a" x))
;; ==> bar

(defun foo (x)
  (bar))
;; ==> foo

(foo 42)
The value of x is 42
;; ==> NIL

x
;; ==> 12

если вы объявите переменную "special" с помощью (defvar ...) он будет обрабатываться по-разному: это как каждый раз, когда вы используете его в качестве параметра или используете его в (let ..) форма, что код будет делать, это сохранение текущего значения, используя новое предоставленное значение, а затем восстановление значения после выхода из функции или let.

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

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

(defvar *x* 12)

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

нет ничего похожего на специальные переменные в C, C++, Java или Python.

последнее замечание о setq и setf. Здесь все немного сложно, потому что вам нужно понять более низкие уровни Lisp, чтобы понять, почему это. Если вы используете Common Lisp, вы должны просто забыть о setq и всегда использовать setf.

setf - это макрос, который будет расширяться до setq при необходимости (однако также setq может меняться в setf при необходимости (макросы символов), и именно здесь все может запутаться для новичка).

ваш последний пример-случай "закрытия". Когда вы определяете функцию (с именем или без имени с (lambda ...) form) функция может "захватывать" видимые переменные и использовать их позже. Более простым случаем, часто показываемым, является "сумматор":

(defun adder (x)
  (lambda (y) (incf x y)))

эта функция возвращает функцию, которая будет добавлять переданное значение для внутренней totalizer:

(let ((f (adder 10)))
  (print (funcall f 3))
  (print (funcall f 9))
  (print (funcall f 11)))

выход будет 13 (10 + 3), 22 (13 + 9) и 33 (22 + 11).

анонимная функция "захватила" локальную переменную x и может использовать его, даже после выхода из . В таких языках, как C, C++ или Java, локальная переменная не может выжить при выходе из области, определившей переменную.

C++11 имеет неназванные функции, но все же переменные не могут быть захвачены и пережить область (они могут быть скопированы в локальный переменные неименованные функции, но это не одно и то же).


на самом деле x является локальным для функции, поэтому setf или setqing он изменяет только локальную переменную x.

чтобы ответить на вторую часть вашего вопроса, есть другой способ определить локальную переменную, отличную от let:

(funcall (lambda (var1 var2...)
           ... use var1 var2 ...)-
   val1 val2 ...)

в самом деле defun может быть реализован как

`(setf (symbol-function ,name) (lambda ,args ,@body))

хотя все реализации, которые я проверил, делают больше.

и symbol-function место позволяет это сделать это:

(setf (symbol-function '+) (function -))

хотя это вообще плохая идея.

второй вопрос

то, что там происходит, это x является локальным для области, содержащей defun. Это не глобальная переменная. Что ты делаешь с этим!--9--> создает закрытие, которое "захватывает" все лексически ограниченные (не созданные с помощью defvar/defparameter) переменные в окружающих областях и придерживается их для будущего. Если хочешь ... проверьте значение x добавить другую функцию

(defun get-x () x)

внутри let. Иными словами,x локальный let что функция в. Ситуация, которую вы предоставили, похожа на:

(let ((x 3))
    (print x)
    (let ((y 7))
        (print (list x y))
        (setf x y))
    (print x))

кроме внутренней let заменить на defun (это lambda).


код x параметр f функция и, следовательно, локальная для функции. Вызов setf не создает новую переменную, а просто устанавливает значение существующей локальной переменной x. Используя setq вместо setf будет вести себя так же.