На этом шаге мы познакомимся с функцией EVAL.
До сих пор мы рассматривали функции, которые в качестве аргументов получали выражения, не являющиеся функциями.
Аргумент, значением которого является функция, называют в функциональном программировании функциональным аргументом, а функцию, имеющую функциональный аргумент - функционалом.
Функция EVAL - это функция, которая может вычислить любое правильно составленное
выражение языка LISP. Например:
$ (EVAL (PLUS 2 3))
5
$ (EVAL (LIST (READ) (READ) (READ)))
PLUS
2
3
5
Говорят, что функция EVAL определяет семантику лисповских форм.
"Лишний" вызов интерпретатора может, например, снять эффект блокировки вычисления при
помощи функции QUOTE или позволяет найти значение значения выражения. Например:
$ (EVAL (QUOTE (PLUS 1 2)))
3
QUOTE и EVAL действуют "во взаимно противоположных направлениях" и как-бы аннулируют эффект друг друга.
Для функции EVAL нет аналогий в языках императивного программирования. Используя EVAL, мы можем выполнить "оператор", который в действительности создан нашей LISP-программой и который может меняться в процессе выполнения программы.
(PRINT (QUOTE _)) % Вывод приглашения % (PRINT (EVAL (READ))) % Ввод выражения и % % вычисление его значения % (PRINT (QUOTE _)) % Повторение вывода приглашения % ... ...Интерпретатор отвечает на заданный ему вопрос и ждет новое задание.
Замечание 2. Во многих LISP-системах имеется функция EVAL-QUOTE,
которая подобна функции EVAL, за исключением того, что имеет два аргумента.
Первый аргумент является именем функции, а второй - списком ее аргументов. Аргументы
EVAL-QUOTE не вычисляются. Функция EVAL-QUOTE может быть определена как:
(EVAL (READ) (READ))
Замечание 3. Необходимо отметить еще одну особенность языка LISP, которая вытекает из природы организации структур данных и программ и механизма их интерпретации. На языке легко реализовывать задачи автоматического синтеза программ. Он позволяет с помощью одних функций формировать определения других функций, программно анализировать и редактировать эти определения как S-выражения, а затем, используя функции типа EVAL, исполнять их.
Замечание 4. Опишем семантику функции EVAL для версии muLISP85.
Если OBJECT - атом, то функция (EVAL OBJECT) возвращает содержимое ячейки значения OBJECT. Т.к. ссылки на новые выбранные атомы автоматические, атомы "вычисляют сами себя" до тех пор, пока не будут связаны с другими значениями.
Если CAR-элемент OBJECT - это имя вычисляемой функции или LAMBDA, то функция (EVAL OBJECT) вычисляет каждый элемент CDR-части OBJECT и добавляет CAR-элемент OBJECT к списку результатов.
Если CAR-элемент OBJECT - имя невычисляемой функции, то функция (EVAL OBJECT) добавляет CAR-элемент OBJECT к CDR-элементу OBJECT без вычисления последнего.
Если CAR-элемент OBJECT - имя макрофункции, то функция (EVAL OBJECT) рекурсивно вычисляет результат добавления CAR-элемента OBJECT к его CDR-элементу.
Если CAR-элемент OBJECT - не функция, то функция (EVAL OBJECT) вызывает ошибку "Неопределенная функция" (UNDEFINED) и генерирует прерывание по ошибке.
На следующем шаге мы введем понятие аппликативного функционала.