[an error occurred while processing this directive]

.цв

16.ДОПОЛНИТЕЛЬНЫЕ АРГУМЕНТЫ И ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ.

.ов

Большинство функций LISP имеет определенное число опрандов и при попытке вызвать функцию с числом аргументов, превышающим допустимое число или с неверными аргументами выдается сообщение об ошибке. В данной главе рассматриваются случаи, где это правило может быть менее строгим. Также показывается, как LISP представляет рабочие переменные, которые можно корректировать и ссылаться на них внутри какой-либо функции, но которые не существуют вне ее. Описанные средства являются характерными для описываемой системы LISP - другие версии LISP могут не содержать возможности иметь дополнительные аргументы и использовать локальные переменные, используя конструкцию PROG.

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

   (DEFUN FNAME (NEEDED (OPTIONAL 1) (OPTIONAL 2))
   ...

Только последние аргументы могут быть дополнительными. В примере, приведенном выше, FNAME является функцией, принимающей от одного до трех аргументов. Если дан один аргумент, то OPTIONAL 1 и OPTIONAL 2 будут по умолчанию нулевыми. Вызов функции с тремя аргументами дает точные значения всем трем параметрам.

Если заголовок функции записан таким образом, но функция вызывается только с минимальным числом аргументов, то дополнительные аргументы могут рассматриваться как локальные переменные. Они могут устанавливаться и возвращаться в исходное состояние внутри тела функции. Любому дополнительному аргументу может быть приписано значение по умолчанию. Иначе говоря, любой локальной переменной может быть дано начальное значение. В показанной выше форме объявления функции взят особый случай, где эти значенияя по умолчанию считаются нулевыми. Полное определение списка аргументов производится таким образом, что что каждый элемент этого списка является атомом и обозначает один из требуемых аргументов функции. В списке, содержащем дополнительные аргументы, CAR является именем параметра, а CDR - значением по умолчанию. Т.о.,

   (DEFUN TAB ((N. 8))
      (LOOP
         (UNTIL (MINUSP (SETQ N (SUB1 N))))
         (PRINC BLANK)))

является функцией с одним дополнительным параметром, N и значением по умолчанию 8. Вызов функции (TAB) приведет к печати 8 пробелов, но если придать TAB числовой аргумент, то можно печатать любое другое число пробелов.

Данное рассмотрение дополнительных аргументов позволяет, например, функции READ иметь один или несколько пропущенных аргументов. Такое рассмотрение не позволяет пользователю создавать конструкции типа PLUS и LIST с произвольным числом аргументов. (см. главу 18).

.цв

17. ЦИКЛЫ.

.ов

В программах часто бывает полезно выполнять одинаковую последовательность операций несколько раз до тех пор, пока не будет удовлетворено некоторое конечное условие. В LISP это часто достигается путем определения некоторых функций, а повторение осуществляется средствами, позволяющими функциям вызывать самих себя. Другим способом такого стиля программирования во всех системах LISP служит некоторый прямой путь написания циклов. Обеспечение построения циклов в представляемой LISP предполагает использованиеодной из основных схем. В то время как определение и название функции могут быть легко трансформируемы с одной версии LISP на другую, описанные в данной главе конструкции не являются общими для всех версий LISP. Для компенсации этого недостатка можно отметить их простоту и ясность и легкость в применении. Повторение последовательности действий выражается на языке LISP функцией LOOP. LOOP рассматривает набор выражений как аргумент. Она обрабатывает их по порядку, повторяя обработку несколько раз. Так, обычно на LISP осуществляется цикл считывание-вычисление-печать следующим образом:

   (LOOP
      (PRINC 'Evaluate:)
      (SETQ U (READ))
      (SETQ U (EVAL U))
      (PRINC 'Value:)
      (PRINT U))

Здесь печатается подсказка, считывается выражение, обрабатывается, печатается сообщение и затем значение; цикл работает до тех пор, пока не будет прерван какой-либо ошибкой.

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

  (LOOP),

которая не указывает LISP ни на какие другие действия, кроме бесконечного повторения.

Результатом такой работы LOOP будет очень медленная работа LISP! В самом деле, в некоторых версиях LISP есть только один способ выхода из этого цикла - нажатие клавиш УПР+СБР.

Чтобы написать циклы, имеющие конец, необходимо использовать функции WHILE и UNTIL. Они обычно написаны внутри тела цикла: о результате другого их использования будет сказано позже.

С своих простейших формах функции WHILE и UNTIL просто определяют условия выхода из цикла, определенного функцией LOOP. WHILE рассматривает предикат как аргумент, и цикл завершается как только его значение будет NIL (т.е. 'ложь'. UNTIL работает аналогично, но выход из цикла осуществляется, когда значение предиката становится ненулевым. Так, для любого предиката P записи для выхода из цикла (WHILE p) и (UNTIL (NOT p) равнозначны. Обратное также верно, т.е. (WHILE (NOT p) равнозначно (UNTIL p). Если UNTIL или WHILE встречается в цикле только один раз, то вышеупомянутое утверждение можно понимать так, что WHILE позволяет циклу работать до тех пор, пока пока значение предиката остается 'истинным', в то время как UNTIL заставляет выйти из цикла, как только предикат становится 'истинным'.

Когда цикл завршен, возвращаемым результатом является значение последнего вычисленного выражения. Это результат содержится W HILE или UNTIL. Каждая из функций выхода из цикла может иметь любое число выражений, следующих за начальным предикатом. Когда обработка предиката показывает, что цикл дожен продолжаться, то эти выражения вычисляются. Последнее из них возвращается как значение функции WHILE (UNTIL), и соответственно, возвращаются как значение цикла. Если за начальным предикатом не следуют выражения, то значение предиката возвращается как результат.

Это можно видеть на примере следующего определения функции, находящей последний элемент списка:

   (DEFUN LAST (L)
      (LOOP
         (UNTIL (NULL (CDR L)) (CAR L))
         (SETQ L (CDR L))))

Цикл перебирает элементы списка до тех пор, пока не будет удовлетворено условие: предикат (NULL (CDR L)) и возвращает значение ((CAR L).

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

При необходимости можно писать и несколько WHILE и UNTIL. Цикл закончится в том случае, когда любое из условий выполняется и возвращаемое значение является значением последнего выражения, которое вычислялось. Так, например, следующий цикл просматривает список L, удаляя все положительные числа. Если прежде чем найти отрицательное число, будет обнаружен какой-либо нечисловой элемент или будет достигнут конец списка, то она возвращает идентификатор, отражающий этот факт. В противном случае, она просматривает список с конца, начиная с первого отрицательного элемента.

   (LOOP
      (UNTIL (NULL L) 'NONE-LEFT)
      (WHILE (NUMBER (CAR L)) 'NON-NUMERIC)
      (UNTIL (MINUSP (CAR L)) L)
      (SETQ L (CDR L)))

Этот цикл не является слишком полезным, но он служит хорошей иллюстрацией использование функций WHILE и UNTIL, работающих одновременно.

Очевидно, циклы могут быть встроенными. WHILE и UNTIL определяют цикл только когда они входят в него.

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

.цв

19. ФУНКЦИИ КАК ОБЪЕКТЫ.

.ов

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

Функциональные аргументы используются для разделения управления работой LISP и точных деталей производимых операаций. Например, часто бывает полезно произвести определенную операцию для всех элементов списка. Это может быть сделано с помощью написания программы, просматривающей весь список при каждой операции. Так, например, для занесения исходных списков ((A B C) (1 2 3) (X Y)) в список (A 1 X), показывая начало всех подсписков, можно определить следующую функцию:

   (DEFUN CARLIST (L) (COND
      (NULL L) NIL)
      (T (CONS (CAAR L) (CARLIST (CDR L))))))

Однако, более общим путем достижения этой цели является определение одной функции MAPC (MAPC является встроенной функцией предлагаемой версии LISP):

   (DEFUN MAPC (FN L) (COND
      ((NULL L) NIL)
      (T (CONS (FN (CAR L))
               (MAPS FN (CDR L))))))

(MAPC CAR x) может быть тогда записана вместо (CARLIST x)

MAPC может принимать в качестве своего первого аргумента либо одну из встроенных функций LISP, (таких как CAR в предыдущем примере), либо функцию, определенную пользователем. Иногда операция, которую необходимо произвести, является достаточно простой для того, чтобы определять новую функцию для нее. В этом случае она может быть записана непосредственно обращением к MAPC, используя при этом специальное обозначение, указывающее на то, что она является неизвестной функцией. Это обозначение представляет функцию со словом LAMBDA, за которым следует список аргументов и затем тело функции. Так, для того, чтобы увеличить все числа в списке L, можно записать:

   (MAPC '(LAMBDA (N) (PLUS N 1)) L)

Аналогично, для печати характеристик списков всех атомов, известных в LISP, можно написать следующее обращение:

   (MAPS '(LAMBDA (ID)
             (PRINT ID '=(PLIST ID)))
         (OBLIST))

Можно было бы записать (MAPC '(LAMBDA (A) (CAR A)) x) вместо (MAPC CAR x) - результат был бы тот же, но точность была бы несколько ниже.

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

Использование функциональных аргументов, приведенное выше, включает символ LAMBDA, являющийся частью LISP, но обычно LAMBDA автоматически присутствует в определениях DEFUN. Если рассматриваемая функция является достаточно простой, то не будет никаких дальнейших трудностей, связанных с ней. Но если функция использует некоторые переменные, которые она не связывает сама, можно столкнуться с некоторыми техническими трудностями. Это относится к способам связывания этих свободных переменных.

Во всех приведенных до сих пор примерах программ LISP переменные, которые использовлись в них были либо глобальными (т.е. установленными с помощью SETQ до ввода программы) или объявленными внутри функций, использующих их.

LISP позволяет любой функции принимать переменные, которые определены любой другой функцией при обращении к ней. В случаях, где несколько функций имеют переменные с общим именем, будет действовать то объявление, которое было сделано последним. Следующий пример иллюстрирует это:

   (DEFUN ADN (N L)
      (MAPC ADD L))

   (DEFUN ADD (P) (PLUS P N))

   (ADDN 3 '(1 5 7))

Здесь последнее обращение к ADDN возвращает результат (4 8 10). Заметьте, что подфункция ADD ссылается на переменную N, которая была определена ADDN. Расмотрим теперь несколько другую версию приведенного выше фрагмета программы, и допустим, что MAPC имеет определение, данное раньше:

   (DEFUN ADDL (L N)
      (MAPC ADD N))

   (DEFUN ADD (P) (PLUS P L))

ADD вызывается изнутри функции MAPC, и обращается к переменной L. К сожалению, MAPC определяет переменную по имени, и ADD должна принять эту переменную, а не L, определенную ADDL. В результате вызывается функция PLUS дя нечислового аргумента и программа не работает. Некоторые системы LISP содержат средства, позволяющие разрешить эту проблему, при этом пользователь всегда помечает функциональные аргументы словом FUNCTION, как например в (MAPC (FUNCTION ADD) N).

FUNCTION не применяется в данной версии LISP, и, поэтому, необходимо пытаться избегать повторное использование имен при употреблении функциональных аргументов, функций, определенных пользователем и свободных переменных одновременно. Версии функций MAP и MAPC, встроенные в интерпретатор LISP организованы таким образом, чтобы не возникало проблем, связанных с повторным употреблением имени. При способе, которым определяется DEFUN использование локальной переменной X означает, что нельзя использовать функцию с именем X.

.цв

20. ВЫВОДЫ.

.ов

Теперь мы рассмотрели все основные свойства языка LISP читателю была предоставлена достаточная информация для построения коротких функций и их тестирования. Нам осталось рассмотреть две последующие главы, касающиеся внутренней организации систем LISP (которые могут быть пропущены при первом чтении) и приложения , содержащие ряд примеров программ LISP, которые являются дополнительной информацией для пользователя ОС ОНИКС, работающего с описываемой версией LISP.

Перед тем, как перейти к изучению технических деталей использования LISP и рассмотрению более сложных примеров полезно повторить и поупражняться с рассмотренными структурами LISP. Полезно повторить, материал, касающийся списковых структур, потренироваться в написании цепочек CAR И CDR. Структуры данных, представляющие функции APPEND, SUBST и другие рассмотренные нами функции, будут образцами списков для ваших упражнений. На основании этих понятий о данных LISP вы можете создать некоторые маленькие функции, следуя правилам, изложенным в главе 12 и 23.

Наряду с функциями, работающими со списками, может оказаться полезно работать с устройствами управления экраном, например с функций VDU, посылающей коды для появления на выходе мерцающих символов двойного размера. Для тех, кто привык к обычной записи обращения к функциям, т.е. записи вида ИМЯ (аргументы), а не к записи вида (ИМЯ аргументы) может представлять определенную трудность скобковая структура LISP. Эти трудности можно преодолеть, внимательно читая и переписывая данные в книге примеры, внося в них небольшие изменения. Полезно также набирать программы на клавиатуре компьютера, используя структурированное расположение текста, аналогичное принятому в данной книге - это поможет вам преодолет трудности, связанные со скобками.

.цв

21. ИСПОЛЬЗОВАНИЕ ПАМЯТИ ВНУТРИ LISP.

.ов

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

Описание, приведенное здесь, не является руководством по внутренней работе какой-то определенной версии LISP. Хотя все, что сказано в этой главе и относится к системе, работающей на ПЭВМ АГАТ, но специфические черты этой системы, связанные с особенностями микропрцессора 6502 не обсуждаются здесь. Одним из достоинств LISP является то, что он может быть на уровне системы разработан множеством несколько различных способов, и эти различия на уровне системы остаются незаметными для пользователяя. Поэтому, за обладателями любой системы LISP остается право изменять внутренние характеристики своей системы в любой ее части. Т.о., информация, касающаяся размещения памяти, полученная из данной главы и внесение изменений в кодовую систему данной версии LISP для ПЭВМ АГАТ может привести к некоторым различиям понимания этого вопроса.

Как было отмечено ранее, LISP работает с указателями. Указатель хранится как адрес в соответствующей области памяти и занимает 2 байта памяти ПЭВМ АГАТ. Очевидно, что ячейка CONS (содержащая два указателя) будет хранится как блок из четырех байт, где первые два байта соответствуют указателю CAR, а последние - указателю CDR. Эта схема была бы достаточно простой, если бы LISP работал только с ячейками CONS и укзателями, которые относились бы только к этим четырех-байтовым блокам.

Учитывая атомы, необходимо иметь дополнительную информацию о каждом указателе, а именно указывающую на то, является ли данный указатель относящимся к ячейке CONS, к идентификатору , адресу части кодовой системы или просто числу. В некоторых машинах допустимая длина слова позволяет включить эту информацию в указатель. На ПЭВМ АГАТ существует дополнительный байт, соответствующий каждой паре слов и биты, составляющие этот байт показывают, как 16-битовые поля CDR и CAR должны быть интерпретированы.

Идентификаторы представляются блоком из пяти байт, аналогично обычным ячейкам CONS. CAR и CDR содержат текущее значения идентификатора и список. Следовательно, в памяти будет содержаться строка символов, составляющих имя идентификатора. Все идентификаторы содержатся в едином блоке памяти, и LISP может найти структуру, соответствующую любому имени, которое передается в ЗУ.

Наибольшую трудность для пользователя представляет способ, которым система LISP располагает в памяти вновь созданные ячейки CONS и каким образом система перераспределяет память, освободившуюся после уничтожения ячеек CONS. Основным в данном процессе является понятие "сбор ненужных данных (мусора)". В данном микропроцессоре ячейки CONS просто распределяются в последовательных байтах по мере необходимости. В некоторый момент объем доступной памяти полностью заполняется; на этом этапе включается "собиратель мусора" (он называется RECLAIM). Первая операция, которую он производит - это поиск ненужных функций. При этом он осуществляет процедуру исключения, т.е. он отмечает те ячейки, которые еще могут понадобиться. Он просматривает все области памяти, где LISP располагает какую-либо информацию и устанавливает бит б каждом блоке памяти. Процедура отметки списка может быть аппроксимирована следующим образом:

   (DEFUN MARK (L) (COND
      ((ALREADY-MARKED L) NIL)
      (T (SET-MARK-BIT L)
         (MARK (CAR L))
         (MARK (CDR L)))))

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

После завершения процесса проставления отметок все непомеченные ячейки могут считаться мусором и память, которую они занимают, может использоваться повторно. Для удобства при перераспределении памяти вся свободная память помещается в один конец памяти. Для этого на место "мусора" помещают некоторую часть активной памяти. При этом находится активная и свободная ячейки, и производится копирование указателей из активных в свободные области. При этом в старую ячейку записываются адреса перехода. Ячейки могут копироваться таким образом до тех пор, пока все "живые" данные не окажутся в одном блоке в определенной области памяти. Затем собиратель мусора в третий раз совершает проход по все указателям LISP. Указатели перераспределенной ячейки должны быть заменены на новые указатели нового расположения данного объекта. Адреса перехода содержат точную информацию для осуществления данного процесса.

Если собиратель мусора не производит никакого перераспределения памяти, то LISP прекращает работу и выдает сообщение об ошибке. Обычно, однако, единственным способом, которым пользователь может судить о работе собирателя мусора является кратковременное приостановление работы LISP. Несмотря на то, что собирание мусора является весьма сложной операцией, она не ведет к заметному замедлению работы LISP до тех пор, пока память не станет слишком ограниченной. По мере того, как программа использует все больший и больший объем памяти, подходя все ближе к предельному значению, определяемому объемом памяти компьютера, собирание мусора необходимо производить все чаще и чаще, и работа программы существенно ухудшается. LISP работает наилучшим образом при достаточном объеме памяти, поэтому при возникновении проблем, связаанных с нехваткой памяти, необходимо искать дополнительную память, уничтожая какие-либо ненужные функции, удаляя их с кассет или диска LISP или установить второй процессор. Для определения необходимости этих шагов можно использовать функцию MESSON, указывающую LISP на необходимость печатать сообщение о каждом проходе собирателя мусора.

Некоторые из книг и статей, представленных в библиографии, содержат подробные описания организации памяти различных систем LISP и различной организации собирателя мусора. Вероятно, наиболее продуктивным яляется способ, разработанный для LISP систем, разработанный технологическим институтом Массачуссетса; Эта система работает с 40 Мбайтами памяти. Как правило, при вычислениях такая память не переполняется, и в задействовании собирателя мусора не возникает необходимости.

.цв

22. ВЫЧИСЛЕНИЯ ВНУТРИ LISP.

.ов

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

Основным вопросом LISP, обсуждаемым здесь, является блок вычислений, т.е. функции EVAL и APPLY. Объяснения будут даны частично описательно, а частично - в кодах LISP. Некоторые детали, касающиеся предлагаемой версии LISP для ПЭВМ АГАТ будут здесь пропущены, т.к нашей целью является показать наиболее типичную работу с LISP, а не создать документацию, являющуюся описанием именно этой версии LISP. Здесь будет использовано некоторое число примитивных функций низкого уровня. Некоторые из них существуют только внутри кодовой системы интерпретатора LISP и обычно являются непосредственно недоступными для пользователя.

Для хранения переменных величин в языке LISP используется понятие канала поверхностной связи. При этом каждый идентификатор имеет соответствующую ему ячейку, и эта ячейка всегдасодержит текущее значение данного идентификатора. Функция SET корректирует эту ячейку. Для чтения ее требуется функция GTS. Если значение ячейки идентификатора явлется первым словом в блоке памяти, представляющем атом, можно записать:

   (DEFUN SET (VAR VAL)
      (RPLACA VAR VAL)
      VAL)

   (DEFUN GTS (VAR)
      (CAR VAR))

Для осуществления данного определения необходимо только использовать соответствующие версии функций RPLACA и CAR, которые не проверяют свои аргументы на то, являются ли их аргументы ячейками CONS. Теперь можно записать функцию EVAL:

   (DEFUN EVAL (X) (COND
      ((ATOM X) (COND
         ((CHARP X)) (GTS X))
         (T X)))
    ...

Данный пример показывает, что идентификаторы подвергаются вычислениям и оценкам при помощи обращений к значениям их ячеек, тогда как все другие виды атомов (т.е. числа) сами подлежат вычислениям. Любой неатомный аргумент EVAL должен определять применение функции. Результаттом функции CAR со списком является функция, где CDR является списком аргументов:

   (T (EVAL-CALL (CAR X) (CDR X)))))

В интерпретации, данной здесь EVAL-CALL будет повторно вычислять свой первый аргумент до тех пор пока не получит значение, которое будет распознаваться как функция. В предлагаемой версии LISP встронная версия EVAL остановитсся и будет выдано сообщение об ошибке, если только она не получит двух распознаваемых функций с двумя вычислениями. Указатели для базовых функций и выражений LAMBDA - основные формы требуемых функций. EVLIS является функцией, вычисляющей все выражения в данном списке.

   (DEFUN EVAL-CALL (FN ARGS) (COND
      ((OR (SUBRP FN)
           (LAMBDAP FN)) (APPLY FN (EVLIS ARGS)))
      ((OR (FSUBRP FN)
           (FLAMBDAP FN)) (APPLY FN (LIST ARGS)))
      (T (EVAL-CALL (EVAL FN) ARGS)))

Тесты SUBR и FSUBRP распознают указатели машинных кодов, преставляющие обычные и специальные функции соответственно, тогда как LAMBDAP и FLAMBDAP распознают обычные и специальные LAMBDA выражения:

   (DEFUN LAMBDAP (FN) (AND
      (NOT (ATOM FN))
      (EQ (CAR FN) 'LAMBDA)
      (OR (NULL (CADR FN)) (NOT (ATOM (CADR FN))))))
   (DEFUN FLAMBDAP (FN) (AND
      (NOT (ATOM FN))
      (EQ (CAR FN) 'LAMBDA)
      (ATOM (CADR FN))
      (NOT (NULL (CADR FN)))))

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

   (DEFUN BIND-VARIABLES (CARD FN) ARGS (CDDR FN)))

   (DEFUN BIND-VARIABLES (BVL ARGS BODY (RESULT) (TEMP))
      (COND
       ((NULL BVL) (LOOP
          (WHILE BODY RESULT (EVAL (CAR BODY))
          (SETQ BODY (CDR BODY))))
       (T (SETQ TEMP (GTS (CAR BVL)))
          (SET (CAR BVL) (CAR ARGS))
          (SETQ RESULT
            (BIND-VARIABLES (CDR BVL) (CDR ARGS) BODY))
           (SET (CAR BVL) TEMP)
           RESULT)))

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

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

В ранних версиях LISP использовался другой подход к связыванию переменных, который назывался 'глубоким связыванием'. Данный способ не требует наличия у переменных ячеек значений. Вместо них все значения содержатся в списке, который объединяет имена переменных и их значения. В результате получается система, функционирующая значительно медленнее, чем та система, которая использует способ связывания переменныых, однако, позволяющая обеспечить функцию соответствующими аргументами. Эта система может быть легко расширена, что позволяет создать сопрограммы, а также структуры, содержащие множество задач и расширенные структуры контроля. Руководство пользователя LISP 1.5 McCarty (см. список литературы) содержит описание интерпретатора, следующего данному стилю.

.сс

[an error occurred while processing this directive]