
- •3. Составные данные.
- •3.1 Списки. Часть I
- •Списком называют любую последовательность допустимых в lisPe элементов, заключенных в круглые скобки.
- •3.1.3 Функция append
- •3.1.4 Функция quote.
- •3.1.5 Функция eval
- •3.2 Точечные пары
- •3.3 Списки. Часть II
- •3.4 Две функции работы со списками
- •Лямбда-выражения и связывающие формы.
- •Для строгости определения приведем бнф для нотации
- •Примеры правильных - выражений:
- •Здесь X связывается с 3, а y остаётся свободной
- •Здесь z связывается с 5, остальные переменные оказываются свободными
- •3.5.1 Lambda – функция в Racket
- •3.5.2 Функция let и локальное -связывание
3.4 Две функции работы со списками
Как правило – все функции работы со списками рекурсивны.
3.4.1 Например, определим функцию length, (она тоже стандартная) возвращающую количество элементов в списке верхнего уровня.
Здесь рекурсия заключается в суммировании. Пустой список содержит 0 элементов, а любой другой - на 1 больше, чем его хвост.
> (define (length lst)
(if (null? lst) 0 (+ 1 (length (cdr lst)))))
> (length (list 2 3 56 7))
4
> (length '(1 2 (3 4) 5))
4
3.4.2 Аналогично определяется функция sum, вычисляющая сумму всех элементов списка. Если список пуст, она равна 0, иначе - сумме первого элемента и всех остальных.
> (define (sum lst)
(if (null? lst) 0 (+ (car lst) (sum (cdr lst)))))
> (sum ‘(2 3 67))
72
Справедливости ради замети, что мы не проверяем, является ли элемент списка числом. Здесь есть потенциальная ошибка и надо её как-то избегать.
Перейдем к одному из самых замечательных и при этом основных особенностей языка LISP: лямбда-выражениям . (Другое написание: -выражение)
Лямбда-выражения и связывающие формы.
Лямбда-выражения – это способ ввести в S-выражения безликость.
Когда мы определяли функции с помощью define, необходимо было задавать функции имя. Но совершенно очевидно, что бывают случаи, когда функция используется лишь однажды и более она не нужна. Для чего нужно далее помнить её имя? Или используется функция, мнемоника которой, гораздо длиннее по написанию, нежели она сама, например, x+1.
Можно привести и другие доводы в пользу, так называемой, безликости (анонимности). Но самое важное в исчислении - это мощный, хорошо формализованный математический аппарат. Он, по сути, и явился толчком для появления языка LISP.
Итак, исчисление – это исчисление анонимных функций. Оно дает:
Метод представления функций
Правило вывода, т.е. правило описывающее получение конечного значения выражения из его формально описанного вида.
Для строгости определения приведем бнф для нотации
<expr> ::= <iden>.<expr> | <expr><expr> | (<expr>) | <aexpr>
<aexpr> ::= <const> | <iden> | <aexpr>
<const> ::= произвольная константа
<iden> ::= любой идентификатор
<aexpr> ::= допустимое арифметическое выражение
Примеры правильных - выражений:
33
(-9)
x.x+1 читается: (заметим, никакого названия у функции нет, она анонимна.)
- функция
x - от x,
. - которая возвращает
x+1 - выражение x+1 или его значение
x.y.2*(x + y)
читается: функция от x, которая возвращает функцию от y, которая возвращает выражение 2*(x+у) или его значение
Символ, стоящий после символа называется связанной переменной. Другие символы в выражении называются свободными (не связанными) переменными.
Чтобы получить значение функции аргументы надо связать с конкретными значениями. Если аргументы не связаны, то их значениями будут являться их имена.
Например.
(x.(x + y))3 = 3 + y
Здесь X связывается с 3, а y остаётся свободной
(z.(x + 2*y + z))5 = x + 2*y + 5
Здесь z связывается с 5, остальные переменные оказываются свободными
Процесс упрощения выражения называется редукцией. Он выражается в изъятии символа соответствующей связанной переменной и подстановкой конкретного значения.
Мы не будем подробно рассматривать эти правила, а только приведем примеры их применения для редукции S-выражений.
Примеры упрощений.
1. (f.(f x)) (y.y) =
Вместо f в тело функции подставляется выражение y.y и поскольку переменная получается связанной, выбрасывается определение функции, т.е. f
= ((y.y) x) =
Теперь можно связать переменную y с её конкретным значением, т.е. x, подставляем в тело функции
= x
2. (x. ( f.f(f x)) (y.(y + 1)) ) 2 =
Видно, что y не может быть связано не с чем, следовательно, должно связываться f с выражением (y.(y + 1))и потом x соответственно с 2.
П
равило
такое: надо
производить самую внутреннюю связку.
Значит выражение f(f
x)изменится, и получится так:
=
(x.
( (y.(y
+ 1)) ( (
y.(y
+ 1)) x) )
) 2 =
Теперь убираем избыточные скобки
= (x. (y.(y + 1)) ( y.(y + 1)) x ) 2 =
Подставляя x, редуцируем самую правую функцию y
= (x. (y.(y + 1)) ( (x + 1)) ) 2 =
Опять убираем избыточные скобки
= (x. (y.(y + 1)) (x + 1) ) 2 =
и редуцируем самую правую функцию y
= (x. ( (x + 1) + 1)) ) 2 =
= (x. ( x + 2 ) ) 2 =
= (2 + 2)
= 4