Россия |
Отображения и функционалы
(defun tuple (x) (cons x x)) (tuple 3) ; = (3 . 3) (tuple 'a) ; = (a . a) (tuple '(Ха)) ; = ((Ха).(Ха))=((Ха)Ха)-это одно и то же!7.5. Пусть дана вспомогательная функция tuple, превращающая любое данное в пару:
Чтобы преобразовать элементы списка с помощью такой функции, пишем сразу:
(defun duble (xl) (map-el #'tuple xl)) ; дублирование элементов (duble '(1(a)()));=((1.1)((a)a)(()))
Немногим сложнее организовать покомпонентную обработку двух списков.
(defun pairl (al vl); Ассоциативный список (cond ; Пока AL не пуст, (al (cons (cons (car al) (car vl)) ; строим пары из "голов". (pairl (cdr al) (cdr vl)) ; Если VL исчерпается, ; то CDR будет давать NIL ) ) ) ) (pairl '(один два two three) '(1 2 два три)) ; = ((один . 1)(два . 2)(two два )(three три ))7.6. Построить ассоциативный список, т.е. список пар из имен и соответствующих им значений, по заданным спискам имен и их значений:
(defun map-comp (fn al vl) ; fn покомпонентно применить ; к соответственным элементам AL и VL (cond (xl (cons (funcall fn (car al) (car vl)) ; Вызов данного FN как функции (map-comp (cdr al) (cdr vl)) ) ) ) )7.7. Определить функцию покомпонентной обработки двух списков с помощью заданной функции fn:
Теперь покомпонентные действия над векторами, представленными с помощью списков, полностью в наших руках. Вот списки и сумм, и произведений, и пар, и результатов проверки на совпадение:
(map-comp #'+ '(1 2 3) '(4 6 9)) ; = (5 8 12 ) - Суммы (map-comp #'* '(1 2 3) '(4 6 9)) ; = (4 12 27)-Произведения (map-comp #'cons '(1 2 3) '(4 6 9)) ; = ((1.4)(2.6)(3.9))-Пары (map-comp #'eq '(4 2 3 ) '(4 6 9)) ; = (T NIL NIL)-Сравнения
Достаточно уяснить, что надо делать с элементами списка, остальное делает отображающий функционал map-comp.
(defun mapf (fl el) (cond ; Пока первый аргумент не пуст, (fl (cons (funcall (car fl) el) ; применяем очередную функцию ; ко второму аргументу (mapf (cdr fl) el) ; и переходим к остальным функциям, ) ) ) ) ; собирая их результаты в общий список (mapf '(length car cdr)'(a b c d));=(4 a(b c d))7.8. Для заданного списка вычислим ряд его атрибутов, а именно - длина, первый элемент, остальные элементы списка без первого.
Композициями таких функционалов можно применять серии функций к списку общих аргументов или к параллельно заданной последовательности списков их аргументов. Естественно, и серии, и последовательности представляются списками.
Безымянные функции
Определения в примерах 4 и 5 не вполне удобны по следующим причинам:
- В определениях целевых функций duble и sqwure встречаются имена специально определенных вспомогательных функций.
- Формально эти функции независимы, значит программист должен отвечать за их наличие при использовании целевых функций на протяжении всего жизненного цикла программы, что трудно гарантировать.
- Вероятно, имя вспомогательной функции будет использоваться ровно один раз - в определении целевой функции.
С одной стороны последнее противоречит пониманию смысла именования как техники, обеспечивающей неоднократность применения поименованного объекта. С другой стороны придумывать хорошие, долго сохраняющие понятность и соответствие цели, имена - задача нетривиальная.
Учитывая это, было бы удобнее вспомогательные определения вкладывать непосредственно в определения целевых функций и обходиться при этом вообще без имен. Конструктор функций lambda обеспечивает такой стиль построения определений. Этот конструктор любое выражение expr превращает в функцию с заданным списком аргументов (x1 ... xK) в форме так называемых lambda-выражений:
( lambda (x1 ... xK) expr )
Имени такая функций не имеет, поэтому может быть применена лишь непосредственно. Defun использует этот конструктор, но, кроме того, дает функциям имена.
(defun sqware (xl)(map-el(lambda(x)(* x x))xl)) (defun duble (xl)(map-el(lambda(x)(cons x x))xl))7.9. Определение функций duble и sqwure из примеров 4 и 5 без использования имен и вспомогательных функций:
Любую систему взаимосвязанных функций можно преобразовать к одной функции, используя вызовы безымянных функций.