Атомы и списки будем называть S – выражениями.
Для представления списков будем использовать совокупность ячеек памяти, каждая из которых содержит два указателя. Первый указатель играет роль указателя на сына, второй – на брата. Например, список (А В) будет иметь вид.
Отсутствие указателя будем обозначать символом y.
Список (* (+ 2 3) с) будет иметь представление:
Чтобы определить выражение по его машинному представлению, нужно просматривать представление, следуя указателям, расположенным слева, на наибольшую глубину и затем по правым указателям (внешний просмотр).
При записи выражения каждой стрелке, не заканчивающейся на атоме, соответствует открывающаяся скобка, каждому символу y - закрывающаяся скобка, каждому атому – сам атом.
Этой схеме соответствует список ((А)(В)).
Вернемся к определению атома. Фактически атомы являются функциями, аргументы которых представляют собой следующие за ними S – выражения. В свою очередь аргумент сам может быть функцией, которую надо вычислить. Поэтому возникает необходимость определять, что представляет собой данный элемент: значение выражения L или символьное имя L какого-то S – выражения. В первом случае перед выражением ставится‘ или указатель QUOTE. Апостроф запрещает вычисление следующего за ним S- выражения, которое воспроизводится без изменений.
Для задания выражений введем функцию Setq:
(Setq <атом> <S – выражение>)
(Setq <атом> ‘<S – выражение>)
В первом случае выражение должно быть вычислено, во втором – на вычисляется. Кроме того, Setq связывает полученный результат с атомом.
(Setq L1 (+ 4 5))
(Setq L1 ‘(+ 4 5))
В первом случае L1 связано с (9), во втором – с (+ 4 5).
Функция CAR возвращает в качестве значения первый элемент списка, то есть его голову. Функция имеет смысл, только для аргументов, являющихся списками, имеющими голову.
(CAR ‘ (A B C)) ® A
(CAR ‘ A) ® ошибка, А не список.
Функция CDR возвращает хвост списка. Если список состоит из одного элемента, то результатом является Nil.
(CDR ‘ (A B C)) ® (B C)
Комбинация вызовов CAR и CDRвыделяет произвольный элемент списка. Для простоты эту комбинацию записывают в виде:
(C…R список)
Вместо многоточия записывается комбинация из букв А (для CAR) и D (для CDR).
(CADDAR L) = (CAR (CDR (CDR (CAR L))))
Функция CONS строит новый список из переданных ему головы и хвоста.
(CONS голова хвост)
(CONS ‘a ‘(b c)) ® (a b c)
(CONS ‘(a b) ‘(c d)) ® ((a b) c d)
(CONS (+ 1 2) ‘(+ 4)) ® (3 + 4)
Здесь первый аргумент без апострофа, поэтому берется как значение.
(CONS ‘(+ 1 2) ‘(+ 4)) ® ((+ 1 2) + 4)
(CONS ‘(a b c) Nil) ® ((a b c))
(CONS Nil ‘(a b c)) ® (Nil a b c)
Предикат ATOMиспользуется для анализа списков, а именно, для идентификации является ли объект атомом или нет.
(ATOM ‘x) ® T
(ATOM (CDR ‘(A B C))) ® Nil. Атом от списка (BC) естественно False.
(ATOM (ATOM (+ 2 3))) ®T.
Результат сложения чисел атом. Т также является атомом.
(EQUAL <S-выр1> <S-выр2>) ®T, если и только если значения обоих выражений равны.
(EQ <S-выр1> <S-выр2>) ®T, если и только если значения обоих выражений равны и представляют один физический список.
(AND <S-выр1> <S-выр2> … <S-вырn>) – если встречается Nil,, возвращается Nil, иначе значение последнего выражения.
(OR <S-выр1> … <S-вырn>) – если при просмотре встречается выражение отличное от Nil, то оно возвращается, иначе Nil.
(NOT <S-выр>) ®T, если и только если значение выражения есть Nil.
Определять функции будем согласно l - исчислению.
(l (x1, x2, …, xn) f)
xi – формальный параметр.
f – тело функции.
Например, функция вычисляющая сумму квадратов будет определяться следующим образом:
(l (x y) (+ (* x x) (* y y))).
Чтобы произвести вычисления будем осуществлять l - вызов.
(l - выражение a1, …, an),
Здесь ai – форма , задающая фактические параметры.
((l (xy) (+ (* xx) (* yy))) 2 3) - дает 13.
Для того чтобы определить новую функцию будем использовать запись:
(DEFUN <имя> <l - выражение>).
Для удобства исключим значок l и внешние скобки.
(DEFUN проценты (часть целое) (* (/ часть целое) 100)).
Вызов:
(проценты 4 20)
Результат: 20.
Условное выражение COND имеет следующий вид:
(COND (p1 a1)
(p2 a2)
. . .
(pn an))
Значение COND определяется следующим образом:
1. Выражения pi вычисляются последовательно слева направо (сверху вниз) до тех пор, пока не встретится выражение, значение которого отлично от Nil.
2. Вычисляется результирующее выражение ai соответствующее этому pi и полученное значение возвращается в качестве результата.
3. Если нет ни одного pi, значение которого отлично от Nil, то значение COND равно Nil/
В более общем виде:
(COND (p1 a1)
…
(pj)
…
(pk ak1 … akn)
…)
В этом случае:
- если условию не ставится в соответствие результирующее выражение, то в качестве результата при истинности предиката выдается само значение предиката;
- если условию соответствует несколько S – выражений, то при его истинности они вычисляются слева направо и результатом будет значение последнего S – выражения.
Реализуем логические связки И, ИЛИ, ®.
(defun И(x y) (COND (x y) (T Nil)))
(defun ИЛИ (x y) (COND (x T) (T y)))
(defun ® (x y) (COND (x y) (T T)))
С помощью COND можно реализовать различные варианты условных выражений.
(if <условие> <то-выр> <иначе-выр>) º (COND (<условие> <то-выр>) (T <иначе-выр>))
Чтобы говорить о некотором свойстве, связанным с некоторым объектом и его значением, будем использовать функцию:
(PUTPROP <атом1> <список> <атом2>).
Эта функция связывает свойства, выраженные списком <атом2>, с конкретным объектом с именем <атом1>. Конкретные значения свойства задаются в объекте <список>.
(PUTPROP ‘Петр ‘(Иван Ирина) ‘Дети).
Чтобы узнать обладает ли объект данным свойством, будем использовать функцию GET:
(GET <атом1> <атом2>) ® значение свойства.
(GET ‘Петр ‘Дети) ® (Иван Ирина)
Теперь сформулируем алгоритм для вычисления списков. Алгоритм должен вычислять значение списка в последовательности, задаваемой указателями – «сыновьями», а затем учитывается последний из встреченных указателей «братьев». После этого учитываются указатели «сыновья», связанные с этим последним, и так далее, пока не получается окончательное значение для данной ветви. Затем вычисления повторяются снова, охватывая выражения более высокого уровня, и так до тех пор пока не будет определено значе ние всего списка. Те части всего выражения, которые ожидают вычисления значений их аргументов, называются квазарами.
Этот алгоритм представим в вида процедуры Eval(S), где S – выражение.
Sub Eval(S)
If S естьатом then
Возврат ¬ значение S
Else
If S1 = QUOTE then
Возврат ¬S2
Else
S1 есть функция
IfS1 указывает на специальную обработку, выполнить эту обработку
Else
Применить Eval ко всем элементам S2 последовательно и затем рекуррентно использовать их найденные значения.
Окончательный возврат ¬S1 (Eval (S2))
End If
End If
EndIf
S1 – первый сын для S. S2 – элементы выражения S, представляющие собой множество братьев выражения S1.
3.4.1 Практические задания
1.Создать машинное представление для списков:
(a (b c) ( (d) e (f)))
( + a (* b (/ (+ c (/ d e)) f)))
(/ (+ ( a (- (b c)))) (* (/ (d b)) a))
(flat (hd (a) flat (tl (a) b)))
(cons (copy (hd (l) )) copy (tl (l)))))
2.Определить значения списков:
(car ‘(a (b c) ((d) e (f))))
(cdr ‘(a (b c) ((d) e (f))))
(cdadr ‘(a (b c) ((d) e (f))))
(consnil ‘(суть пустой список))
(cons (car ‘(a b))(cdr ‘(a b)))
(car ‘(car (a b c)))
(cdr (car (cdr ‘(a b c))))
3.Запишите последовательность вызовов CAR и CDR, выделяющих из приведенных списков символ «цель». Упростите эти последовательности с помощью функций C…R.
(1 2 цель 3 4)
((1) (2 цель ) (3 4 цель)))
((1 (2 (3 4 цель))))
4.Определить вид списка, имеющего следующее машинное представление:
4. Реализовать с помощью COND логические операции: эквивалентность, штрих Шеффера, стрелка Пирса.
5. Реализовать функцию reverse, переставляющую местами элементы списка.
3.4.2 Компьютерное задание
Создать БД для индуктивного вывода и реализовать программу заполнения этой базы для оператора Setq. Возможная структура таблицы для хранения списков:
Имя поля | Тип | Комментарий |
List | Text | Наименование списка |
BeginList | Boolean | Является ли началом списка, если да -TRUE |
SonPointer | Long | Указатель на сына |
BrotherPointer | Long | Указатель на брата |
ValueEl | Text | Значение. Для указателей Nil |
ValueType | Text | Тип значения: ячейка, список, пустой список, атом, функция |
Пример заполнения:
Пусть последовательно создаются 2 списка:
Setq a (* (+ 2 3) c)
Setq Second ((a) (b))
Структура списков: Список а
Список Second
Таблица для этих списков будет иметь вид:
List | BeginList | SonPointer | BrotherPointer | ValueEl | ValueType |
a | true | 2 | 4 | Nil | ячейка |
a | false | 3 | 0 | Nil | ячейка |
a | false | 0 | 0 | * | функция |
a | false | 5 | 11 | Nil | ячейка |
a | false | 6 | 7 | Nil | ячейка |
a | false | 0 | 0 | + | функция |
a | false | 8 | 9 | Nil | ячейка |
a | false | 0 | 0 | 2 | атом |
а | false | 10 | 0 | Nil | ячейка |
а | false | 0 | 0 | 3 | атом |
а | false | 12 | 0 | Nil | ячейка |
a | false | 0 | 0 | c | Пустой список |
Second | true | 14 | 16 | Nil | ячейка |
Second | false | 15 | 0 | Nil | ячейка |
Second | false | 0 | 0 | a | список |
Second | false | 17 | 0 | Nil | ячейка |
Second | false | 18 | 0 | Nil | ячейка |
Second | false | 0 | 0 | b | Пустой список |
Порядок выполнения