Опубликован: 19.03.2004 | Уровень: специалист | Доступ: платный
Лекция 5:

Имена, определения и контексты

< Лекция 4 || Лекция 5: 123 || Лекция 6 >

Именование значений и подвыражений

Переменные

Переменная — это символ, который используется для представления аргумента функции.

Таким образом, возвращаясь к Дж.Маккарти [1]:

"Можно написать "a + b, где a = 341 и b = 216". В такой ситуации не может быть недоразумений, и все согласятся, что ответ есть 557. Чтобы получить этот результат, необходимо заменить переменные фактическими значениями, и затем сложить два числа (на арифмометре, например).

Одна из причин, по которой в этом случае не возникает недоразумений, состоит в том, что "a" и "b" не есть приемлемые входы для арифмометра, и следовательно, очевидно, что они только представляют фактические аргументы. При символьных вычислениях ситуация может быть более сложной. Атом может быть как переменной, так и фактическим аргументом. К дальнейшему усложнению приводит то обстоятельство, что некоторые из аргументов могут быть переменными, вычисляемыми внутри вызова другой функции. В таких случаях интуитивный подход может подвести. Для эффективного программирования в функциональном стиле необходимо более точное понимание формализмов.

Чтобы не обескураживать читателей, следует заметить, что здесь ничего принципиально нового нет. Все, что сейчас рассматривается, может быть логически выведено из правил представления программ в виде S-выражений или из определения универсальных функций eval/apply, и является их непосредственным следствием, возможно, не вполне очевидным.

Любой формализм для переменных сводится к лямбда-обозначению. Часть интерпретатора, которая при вычислении функций связывает переменные, называется APPLY. Когда APPLY встречает функцию, начинающуюся с LAMBDA, список переменных попарно связывается со списком аргументов и добавляется к началу а-списка. При вычислении функции могут быть обнаружены переменные. Они вычисляются поиском в а-списке. Если переменная встречается несколько раз, то используется последнее или самое новое значение. Часть интерпретатора, которая делает это, называется EVAL. Проиллюстрируем данное рассуждение на примере. Предположим, что интерпретатор получает следующее S-выражение:

((LAMBDA (X Y) (CONS X Y)) 'A 'B)

Функция: ((LAMBDA (X Y) (CONS X Y)) 'A 'B)

Аргументы: (A B)

EVAL через EVAL-A передает эти аргументы функции APPLY. (См. лек. 3).

(APPLY #'(LAMBDA (X Y) (CONS X Y)) '(A B) Nil )

APPLY свяжет переменные и передаст функцию и удлинившийся а-список EVAL для вычисления.

(EVAL  '(CONS X Y) ' ((X . A) (Y . B) . Nil))

EVAL вычисляет переменные и сразу передает их консолидации - функции CONS, строящей из них бинарный узел.

(Cons 'A 'B) = (A . B)

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

Реальный интерпретатор пропускает один шаг, требуемый формальным определением универсальных функций".

На практике сложилась традиция включать в систему функционального программирования специальные функции, обеспечивающие иерархию контекстов, в которых переменные обладают определенным значением. Для Clisp это LET и LET *.

LET сопоставляет локальным переменным независимые выражения. С ее помощью можно вынести из сложного определения любые совпадающие подвыражения.

(DEFUN UNION (x y) 

    (LET ( (a-x (CAR x))
            (d-x (CDR x))
          )   ; конец списка локальных именованных значений

       (COND ((NULL x) y)
                  ((MEMBER a-x y) (UNION d-x y) )  ; использование локальных
                  (T (CONS a-x (UNION d-x y)) )      ; значений из контекста
    )   )  ; завершение контекста LET 
)

LET * — сопоставляет локальным переменным взаимосвязанные выражения. Она позволяет дозировать сложность именуемых подвыражений.

(DEFUN MEMBER (a x)
      (LET* ( (N-X (NULL x))
	(a-x (CAR x))
	(d-x (CDR x))
	(e-car (EQ a a-x))
              ) ; список локально именованных выражений

         (COND (N-X Nil)                   ; использование
                    (E-CAR T)                 ; именованных
                    (T (MEMBER A D-X))  ; выражений
      )  )   ; выход из контекста именованных выражений
)

(Эквивалентность с точностью до побочного эффекта.)

Глобальные переменные можно объявить с помощью специальной функции DEFPARAMETER.

(DEFPARAMETER GLOB '(a b c))

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

(LET ((GLOB 12))(PRINT GLOB))
(PRINT GLOB)

напечатано будет:

12
(A B C)

Константы

Дж.Маккарти обращает внимание [1]:"Иногда говорят, что константы представляют сами себя, в противоположность переменным, представляющим что-то другое. Это не вполне точно, поскольку обычно константы представляют как a, b, c, ..., а переменные как x, y, z, ... — но и то, и другое выглядит как атом. Удобнее говорить, что одна переменная ближе к константам, чем другая, если она связана на более высоком уровне и ее значение изменяется не столь часто.

Обычно переменная считается связанной в области действия лямбда-конструктора функции, который связывает переменную внутри тела определения функции путем размещения пары из имени и значения в начале а-списка. В том случае, когда переменная всегда имеет определенное значение независимо от текущего состояния а-списка, она будет называться константой. Такую неизменяемую связь можно установить, помещая пару (a . v) в конец a-списка. Но в реальных системах это организовано с помощью так называемого списка свойств атома, являющегося представлением переменной. Каждый атом имеет свой p-список (property list), доступный через хэш-таблицу идентификаторов, что действует эффективнее, чем a-список. С каждым атомом связана специальная структура данных, в которой размещается имя атома, его значение, определение функции, представляемой этим же атомом, и список произвольных свойств, помеченных индикаторами. При вычислении переменных EVAL исследует эту структуру до выполнения поиска в а-списке. Такое устройство переменных не позволяет им служить в а-списке".

Константы могут быть заданы программистом. В системе Clisp чтобы переменная X стала обозначением для (A B C D), надо воспользоваться псевдо-функцией DEFCONSTANT.

(DefConstant X '(A B C D))

Особый интерес представляет тип констант, которые всегда обозначают себя, Nil — пример такой константы. Такие константы как T, Nil и другие самоопределимые константы (числа, строки) не могут использоваться в качестве переменных. Cмысл чисел и строк не может быть изменен с помощью DEFCONSTANT.

< Лекция 4 || Лекция 5: 123 || Лекция 6 >
Дарья Федотова
Дарья Федотова
Сергей Березовский
Сергей Березовский

В рамках проф. переподготовки по программе "Программирование"

Есть курсы, которые я уже прошел. Но войдя в курс я вижу, что они не зачтены (Язык Ассемблера и архитектура ЭВМ, Программирование на С++ для профессионалов). Это как?

Алина Ленкова
Алина Ленкова
Россия, Ставрополь, СФ МГУПИ, 2014
Валерий Ромашов
Валерий Ромашов
Россия