LINUX.ORG.RU

CL: привязки


0

1

Читаю главу про переменные. http://lisper.ru/pcl/variables То, что существуют лексические и динамические переменные понял. Наверное. Если перевеси на термины С- подобных языков, корректно же сказать, что лексические переменные это локальные переменные, а динамические это глобальные?

Не пойму, что такое лексическая привязка и динамическая привязка? Там в книге всё крутится вокруг кода

(let ((*x* 10))
  (foo))

где *x* была определана ранее с помощью defvar. В данном случае это динамическая привязка? Потому что *x* это динамическая переменная?

Правильно сказать в терминах С- подобных языков, что в данном случае происxодит скрытие глобальной переменной в ф-и foo т.к. в её области видимости создали локальную переменную с тем же именем?

Если есть несколько потоков, если в момент времени t один поток зашёл в приведённый выше let всем потокам достанется изменённое значение *x* равное 10? Если да, это главное различие с С-подобными языками, там данный код это всего лишь создание локальной переменной *x* с которой работает только foo, а в CL это изменение привязки влияющее на всё, что работает с переменной *x*?

Вот это совсем не понял:

Ранние реализации Lisp использовали динамические переменные в качестве локальных, по крайней мере при интерпретации. Elisp, диалект Lisp, используемый в Emacs, является в некоторой мере устаревшим в этом отношении, продолжая поддерживать только динамические переменные. Некоторые другие языки повторили этот путь от динамических к лексическим переменным: например, локальные переменные в Perl являются динамическим, в то время как my-переменные, введенные в Perl 5 — лексические. Python никогда не имел настоящих динамических переменных, но ввел настоящую лексическую область видимости только с версии 2.2. (Лексические переменные Python все еще ограничены в сравнении с Lisp из-за объединения присваивания (assignment) и связывания (binding) в синтаксисе языка.)

1. Динамические переменные в качестве локальных. Имеется в виду что не было локальных и только это?

2. «локальные переменные в Perl являются динамическим, в то время как my-переменные, введенные в Perl 5 — лексические» - в перле если переменная не объявлена как my она глобальная, именно это имеется в виду?

3. «Python никогда не имел настоящих динамических переменных, но ввел настоящую лексическую область видимости только с версии 2.2.» - питон не имел глобальных переменных, но что имеется в виду под настоящими лексическими? т.е. были локальные которые не являлись лексическими? Что это значит?

Ответ на: комментарий от archimag

Все глобальные переменные в Common Lisp являются динамическими! Глобальных лексических переменных в Common Lisp нет, хотя их можно имитировать с помощью макросимволов.

Непонятен смысл глобальной лексической переменной, какими свойствами она обладала бы?

declare special требуется всегда, когда в let объявляется динамическая переменная или только тогда, когда динамическая переменная не была ещё объявлена с помощъю defvar defparameter?

TirNaNOg ()

Если есть несколько потоков, если в момент времени t один поток зашёл в приведённый выше let всем потокам достанется изменённое значение *x* равное 10?

Нет, динамическая привязка специальной переменной распространяется только на один поток.

1. Динамические переменные в качестве локальных. Имеется в виду что не было локальных и только это?

Это скорее имеет отношение к замыканиям - отсутствие лексических переменных приводит к тому, что нельзя захватить и передать лексический контекст в замыкание.

dmitry_vk ★★★ ()

Динамические переменные это не глобальные, а просто такие, которые имеют динамическую область видимости, их значение, и то, откуда его брать, определяется в рантайме.(в противоположность лексической области видимости(ее еще называют статической областью видимости), где значение переменных видно только как бы «ниже по коду»)

Связывание происходит или через формы биндинга лексических переменных с декларацией о том, что переменная динамическая, или через специальный оператор PROGV.

(defun foo ()
  (declare (special *x*))
  (print (* *x* *x*)))

(defun bar ()
  (progv '(*x*) '(2)
    (foo)))

(defun baz ()
  (let ((*x* 3))
    (foo)))

;(let ((*x* 3))
;  (declare (special *x*))
;  (foo))
;==> 9
;
;(bar)
;==> 4
;
;(baz)
;==> debugger invoker on UNBOUND-VARIABLE: The variable *x* is unbound.

В Emacs Lisp let организовывает динамические биндинги. Т.е:

(defun foo () (* x x))

(defun bar () (let ((x 5)) (foo)))

;(bar)
;==> 25

Love5an ()
Ответ на: комментарий от Love5an

DEFVAR и DEFPARAMETER отличаются тем, что объявляют переменную специальной в глобальном контексте

т.е. напр. (defparameter *x* 123) аналогичен

(declaim (special *x*))
(setf *x* 123)

Love5an ()
Ответ на: комментарий от Love5an

Отличается о чего? Поведение этого кода не определено и зависит от реализации. Об этом написано в статье, на которую я указал в начале: http://lisper.ru/articles/cl-vars.

archimag ★★★ ()
Ответ на: комментарий от TirNaNOg

> Непонятен смысл глобальной лексической переменной,

какими свойствами она обладала бы?


Была бы как обычная глобальная переменная в «других» языках. Использование подобных перемен считается плохой практикой, вероятно поэтому Common Lisp их и не поддерживает :)

Основная проблема «традиционных глобальных переменных» - их изменение влияет сразу на весь код. В Common Lisp форма let позволяет контролировать область для которой происходит изменение динамической переменной.

declare special требуется всегда, когда в let объявляется

динамическая переменная или только тогда, когда динамическая


переменная не была ещё объявлена с помощъю defvar defparameter?



Для переменных, объявленных с помощью defvar и defparameter больше ничего не требуется. На самом деле, для начала можно вообще забыть про declare special, ибо это встречается весьма редко и мне кажется не очень хорошей практикой.

archimag ★★★ ()

вот простой пример:

динамическое связывание:

(defvar x 1) 
; => X 
(defun foo () x) 
; => FOO 
(foo) 
; => 1 
(let ((x 2)) (foo)) 
; => 2 
(let ((x 1)) 
  (defun bar () x)) 
; => BAR 
(bar) 
; => 1 
(let ((x 2)) (bar)) 
; => 2 

лексическое:

(defvar x 1)
; => X
(defun foo () x)
; => FOO
(foo)
; => 1
(let ((x 2)) (foo))
; => 2
(let ((y 1))
  (defun bar () y))
; => BAR
(bar)
; => 1
(let ((y 2)) (bar))
; => 1

korvin_ ★★★★★ ()
Ответ на: комментарий от archimag

нет, именно этого кода - вполне определено
потому что мы тут вполне конкретно определяем переменную через декларацию

Love5an ()

Присоединяюсь к последнему вопросу.

«Python никогда не имел настоящих динамических переменных, но ввел настоящую лексическую область видимости только с версии 2.2.» - Что это значит?

naryl ★★★★★ ()
Ответ на: комментарий от naryl

Видимо, согласно тому, что понял из ответов это означает, что:

1. нет динамических переменных - нет способа объявить в двух и больше функциях общую для них переменную (не передавая её в вызовах)

2. замыкания появились только в питоне 2.2, до него переменные не были лексическими, а просто какими-то.

2love5un:

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

1. Корректно ли утверждать, что от глобальных переменных C- подобных языков их отличает только особенность синтаксиса лиспа, которая требудет для их использования в функции явно указать, что «здесь используется динамическая переменная»:

(defun foo () 
  (declare (special *x*)) 
  (print (* *x* *x*))) 

? Т.е., к примеру в PHP, есть слово global:

$a = 10;

function foo()
{
  global $a;
  print $a; // 10
}

это одно и тоже с динамическими переменными лиспа?

2. «Специальная» это синоним «динамическая» или есть какие-то отличия?

TirNaNOg ()
Ответ на: комментарий от TirNaNOg

1. Корректно ли утверждать, что от глобальных переменных C- подобных языков их отличает только особенность синтаксиса лиспа, которая требудет для их использования в функции явно указать, что «здесь используется динамическая переменная»:

Нет, это совсем разные вещи. Вот еще пример:

(defun foo ()
  (declare (special x))
  (incf x)
  (format t "FOO: ~a~%" x))

(defun bar ()
  (let ((x 2))
    (declare (special x))
    (format t "Before FOO: ~a~%" x)
    (foo)))

(defun baz ()
  (let ((x 1))
    (declare (special x))
    (format t "Before BAR: ~a~%" x)
    (bar)
    (format t "After BAR: ~a~%" x)))

(defun quux ()
  (let ((x 123))
    (labels ((f ()
               (let ((x 2))
                 (format t "X in f: ~a~%" x)
                 (g)))
             (g ()
               (format t "X in g: ~a~%" x)))
      (format t "X before f: ~a~%" x)
      (f))))

(defun main ()
  (write-line "X as dynamic variable:")
  (baz)
  (write-line "X as lexical variable:")
  (quux))
X as dynamic variable:
Before BAR: 1
Before FOO: 2
FOO: 3
After BAR: 1
X as lexical variable:
X before f: 123
X in f: 2
X in g: 123

2. «Специальная» это синоним «динамическая» или есть какие-то отличия?

Синоним.

Love5an ()
Ответ на: комментарий от Love5an

Долго писал, раза три совершенно разные тексты с совершенно разными блоками кода. Но в конце-концов все различия в работе лексических и динамических переменных уяснил. Наверно. ;) Не понял только, почему динамические переменные нельзя считать аналогом глобальных в С- подобных языках. Потому, что существует механизм привязки в форме let, который на время действия let заместит привязку? Но оно как бы несколько отдельный механизм от собственно свойств динамических переменных. А все свойства это:

1. получить доступ можно откуда угодно
2. видны в вызываемых функциях, НО!

в таком виде написанныx ф-ий и лексические переменные видны в вызываемых функциях:

(defun s1 ()
  (let ((s 1))
    (labels ((s2 ()
               (format t "S in s2: ~a~%" s)
               (s3))
      (s3 ()
          (format t "S in s3: ~a~%" s)))
    (format t "S in s1: ~a~%" s)
    (s2))))

3. в замыканиях связывания динамических переменных не запоминаются, при обращении к переменной её значение берётся из вызываемого окружения

Вроде как, все свойства.

А лексические переменные это... лексические переменные. Запоминаются в замыканиях, в остальном просто аналог локальныx переменныx в других языках.

TirNaNOg ()
Ответ на: комментарий от TirNaNOg

>Не понял только, почему динамические переменные нельзя считать аналогом глобальных в С- подобных языках.
Динамические переменные могут быть и *локально* динамическими, т.е. в другом месте - совершенно не динамическими, что я в своем примере и показал. Плюс, динамические переменные точно как и лексические организовывают «наложение» в форме стека, но, в отличие от лексических, их биндинги видны не только «ниже по коду», но и вообще откуда угодно из стека вызовов снизу.

А лексические переменные это... лексические переменные. Запоминаются в замыканиях, в остальном просто аналог локальныx переменныx в других языках.

да

Love5an ()
Ответ на: комментарий от TirNaNOg

Но оно как бы несколько отдельный механизм от собственно свойств динамических переменных.

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

procedure foo(x,format)
var savDateFormat:string; 
begin
savDateFormat:=global.DateFormat;
global.DateFormat:=format; 
try 
  printDate(x); 
finally
  global.DateFormat:=savDateFormat; 
  end; 
end;

Специальные переменные позволяют писать его просто вот так (язык - воображаемый).

procedure foo(x,format);
begin
let global.DateFormat format; 
printDate(x); 
end; 

И есть ещё одна фишка: в многопоточных реализациях в каждом треде - своя привязка специальной переменной.

den73 ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.