Гибкий интерпретатор
В качестве примера повышения гибкости определений приведено упрощенное определение Лисп-интерпретатора на Лиспе, полученное из М-выражения, приведенного Дж. Мак-Карти в описании Lisp 1.5
[1 (не найдено)].
Ради удобочитаемости здесь уменьшена диагностичность, нет предвычислений и формы Prog. Лисп хорошо приспособлен к оптимизации программ. Любые совпадающие подвыражения можно локализовать и вынести за скобки, как можно заметить по передаче значения
"(ASSOC e al )".
Определения функций хранятся в ассоциативном списке, как и значения переменных.
Функция SUBR — вызывает примитивы, реализованные другими, обычно низкоуровневыми, средствами. ERROR — выдает сообщения об ошибках и сведения о контексте вычислений, способствующие поиску источника ошибки. Уточнена работа с функциональными аргументами:
(DEFUN EVAL (e al ) (COND
((EQ e NIL ) NIL ) ((ATOM e )((LAMBDA (v ) (COND (v (CDR v ) ) (T (ERROR 'undefvalue )) ) ) (ASSOC e al ) ) ) ((EQ (CAR e) 'QUOTE ) (CAR (CDR e )) ) ((EQ (CAR e) 'FUNCTION ) (LIST 'FUNARG (CADR fn ) al ) ) ((EQ (CAR e) 'COND ) (EVCON (CDR e ) al ) ) (T (apply (CAR e)(evlis (CDR e) al ) al ) ) ) )
(DEFUN APPLY (fn args al ) (COND ((EQ e NIL ) NIL ) ((ATOM fn ) (COND ((MEMBER fn '(CAR CDR CONS ATOM EQ )) (SUBR fn agrs al )) (T (APPLY (EVAL fn al ) args al )) ) )
((EQ (CAR fn ) 'LABEL ) (APPLY (CADDR fn ) args (CONS (CONS (CADR fn )(CADDR fn )) al ) ) )
((EQ (CAR fn ) 'FUNARG ) (APPLY (CDR fn ) args (CADDR fn)) )
((EQ (CAR fn ) 'LAMBDA ) (EVAL (CADDR fn ) (APPEND (PAIR (CADR fn ) args ) al )) (T (APPLY (EVAL fn al ) args al )) ) )
Определения assoc, append, pair, list — стандартны.
Примерно то же самое обеспечивают eval-p и apply-p, рассчитанные на использование списков свойств атома для хранения постоянных значений и функциональных определений. Индикатор category указывает в списке свойств атома на правило интерпретации функций, относящихся к отдельной категории, macro — на частный метод построения представления функции. Функция value реализует методы поиска текущего значения переменной в зависимости от контекста и свойств атомов.
(defun eval-p (e c) (cond ((atom e) (value e c)) ((atom (car e))(cond ((get (car e) 'category) ((get (car e) 'category) (cdr e) c) ) (T (apply-p (car e)(evlis (cdr e) c) c)) ) ) (T (apply-p (car e)(evlis (cdr e) c) c)) ))
(defun apply-p (f args c) (cond ((atom f)(apply-p (function f c) args c)) ((atom (car f))(cond ((get (car f) 'macro) (apply-p ((get (car f) 'macro) (cdr f) c) args c)) (T (apply-p (eval f e) args c)) ) ) (T (apply-p (eval f e) args c)) ))
Или то же самое с вынесением общих подвыражений во вспомогательные параметры:
(defun eval-p (e c) (cond ((atom e) (value e c)) ((atom (car e)) ((lambda (v) (cond (v (v(cdr e) c) ) (T (apply-p (car e)(evlis (cdr e) c) c)) )) (get (car e) 'category) ) ) (T (apply-p (car e)(evlis (cdr e) c) c)) ))
(defun apply-p (f args c) (cond ((atom f)(apply-p (function f c) args c)) ((atom (car f)) ((lambda (v) (cond (v (apply-p (v (cdr f) c) args c)) (T (apply-p (eval f e) args c)) ))(get (car f) 'macro) )) (T (apply-p (eval f e) args c)) ))
Расширение системы программирования при таком определении интерпретации осуществляется простым введением/удалением соответствующих свойств атомов и функций.
Полученная схема интерпретации допускает разнообразные категории функций и макросредства конструирования функций, позволяет задавать различные механизмы передачи параметров функциям. Так, в языке Clisp различают, кроме обычных, обязательных и позиционных, — необязательные (факультативные), ключевые и серийные(многократные, с переменным числом значений) параметры . Виды параметров обозначаются пометкой &optional, &key и &rest соответственно, размещаемой перед параметрами в
lambda списке формальных аргументов. При этом серийный параметр должен быть последним в списке.
(defun funcall (fn rest agrs) (apply fn args))
Необязательный параметр может иметь начальное значение, устанавливаемое по умолчанию, т.е. если этот параметр не задан при вызове функции. При отсутствии начального значения его роль играет Nil.
(defun ex-opt (space optional dot (line 'x)) (list space 'of dot 'and- line)) (ex-opt 'picture) (ex-opt 'picture 'circle) (ex-opt 'picture 'circle 'bars)
Пример 6.2.
Ключевые параметры, являясь необязательными, не зависят еще и от порядка вхождения в список аргументов. Незаданные аргументы по умолчанию связываются с Nil.
(defun keylist (a key x y z) (list a x y z)) (keylist 1 :y 2) ;= (1 NIL 2 NIL)
(defun LENGTH (L optional (V 0)) (cond ((null L) V) (T (+ 1 (LENGTH (cdr L)))) ) )
((LENGTH '(1 2) 3) ; = 5
(defun REVERSE (L optional (m Nil)) (cond ((null L) m) (T (REVERSE (cdr L) (cons (car L) m) )) ) )
(REVERSE '(1 2 3)) ; = (3 2 1) (REVERSE '(1 2 3) '(5 6)) ;= (3 2 1 5 6)
Пример 6.3.
Такой подход к работе параметрами часто освобождает от необходимости во вспомогательных функциях, что упрощает и определение Eval от обязательности упоминания а-списка. Если воспользоваться сводимостью (COND) к Nil при отсутствии истиного предиката и кое-где использовать отображения, то определение становится совсем компактным.