- •Оглавление
- •Предисловие
- •Предисловие к русскому изданию
- •Глава 1. Введение
- •1.1. Новые инструменты
- •1.2. Новые приемы
- •1.3. Новый подход
- •Глава 2. Добро пожаловать в Лисп
- •2.1. Форма
- •2.2. Вычисление
- •2.3. Данные
- •2.4. Операции со списками
- •2.5. Истинность
- •2.6. Функции
- •2.7. Рекурсия
- •2.8. Чтение Лиспа
- •2.9. Ввод и вывод
- •2.10. Переменные
- •2.11. Присваивание
- •2.12. Функциональное программирование
- •2.13. Итерация
- •2.14. Функции как объекты
- •2.15. Типы
- •2.16. Заглядывая вперед
- •Итоги главы
- •Упражнения
- •Глава 3. Списки
- •3.1. Ячейки
- •3.2. Равенство
- •3.3. Почему в Лиспе нет указателей
- •3.4. Построение списков
- •3.5. Пример: сжатие
- •3.6. Доступ
- •3.7. Отображающие функции
- •3.8. Деревья
- •3.9. Чтобы понять рекурсию, нужно понять рекурсию
- •3.10. Множества
- •3.11. Последовательности
- •3.12. Стопка
- •3.13. Точечные пары
- •3.14. Ассоциативные списки
- •3.15. Пример: поиск кратчайшего пути
- •3.16. Мусор
- •Итоги главы
- •Упражнения
- •Глава 4. Специализированные структуры данных
- •4.1. Массивы
- •4.2. Пример: бинарный поиск
- •4.3. Строки и знаки
- •4.4. Последовательности
- •4.5. Пример: разбор дат
- •4.6. Структуры
- •4.7. Пример: двоичные деревья поиска
- •4.8. Хеш-таблицы
- •Итоги главы
- •Упражнения
- •Глава 5. Управление
- •5.1. Блоки
- •5.2. Контекст
- •5.3. Условные выражения
- •5.4. Итерации
- •5.5. Множественные значения
- •5.6. Прерывание выполнения
- •5.7. Пример: арифметика над датами
- •Итоги главы
- •Упражнения
- •Глава 6. Функции
- •6.1. Глобальные функции
- •6.2. Локальные функции
- •6.3. Списки параметров
- •6.4. Пример: утилиты
- •6.5. Замыкания
- •6.6. Пример: строители функций
- •6.7. Динамический диапазон
- •6.8. Компиляция
- •6.9. Использование рекурсии
- •Итоги главы
- •Упражнения
- •Глава 7. Ввод и вывод
- •7.1. Потоки
- •7.2. Ввод
- •7.3. Вывод
- •7.4. Пример: замена строк
- •7.5. Макрознаки
- •Итоги главы
- •Упражнения
- •Глава 8. Символы
- •8.1. Имена символов
- •8.2. Списки свойств
- •8.3. А символы-то не маленькие
- •8.4. Создание символов
- •8.5. Использование нескольких пакетов
- •8.6. Ключевые слова
- •8.7. Символы и переменные
- •8.8. Пример: генерация случайного текста
- •Итоги главы
- •Упражнения
- •Глава 9. Числа
- •9.1. Типы
- •9.2. Преобразование и извлечение
- •9.3. Сравнение
- •9.4. Арифметика
- •9.5. Возведение в степень
- •9.6. Тригонометрические функции
- •9.7. Представление
- •9.8. Пример: трассировка лучей
- •Итоги главы
- •Упражнения
- •Глава 10. Макросы
- •10.1. Eval
- •10.2. Макросы
- •10.3. Обратная кавычка
- •10.4. Пример: быстрая сортировка
- •10.5. Проектирование макросов
- •10.6. Обобщенные ссылки
- •10.7. Пример: макросы-утилиты
- •10.8. На Лиспе
- •Итоги главы
- •Упражнения
- •Глава 11. CLOS
- •11.1. Объектно-ориентированное программирование
- •11.2. Классы и экземпляры
- •11.3. Свойства слотов
- •11.4. Суперклассы
- •11.5. Предшествование
- •11.6. Обобщенные функции
- •11.7. Вспомогательные методы
- •11.8. Комбинация методов
- •11.9. Инкапсуляция
- •11.10. Две модели
- •Итоги главы
- •Упражнения
- •Глава 12. Структура
- •12.1. Разделяемая структура
- •12.2. Модификация
- •12.3. Пример: очереди
- •12.4. Деструктивные функции
- •12.5. Пример: двоичные деревья поиска
- •12.6. Пример: двусвязные списки
- •12.7. Циклическая структура
- •12.8. Неизменяемая структура
- •Итоги главы
- •Упражнения
- •Глава 13. Скорость
- •13.1. Правило бутылочного горлышка
- •13.2. Компиляция
- •13.3. Декларации типов
- •13.4. Обходимся без мусора
- •13.5. Пример: заранее выделенные наборы
- •13.6. Быстрые операторы
- •13.7. Две фазы разработки
- •Итоги главы
- •Упражнения
- •Глава 14. Более сложные вопросы
- •14.1. Спецификаторы типов
- •14.2. Бинарные потоки
- •14.3. Макросы чтения
- •14.4. Пакеты
- •14.5. Loop
- •14.6. Особые условия
- •Глава 15. Пример: логический вывод
- •15.1. Цель
- •15.2. Сопоставление
- •15.3. Отвечая на запросы
- •15.4. Анализ
- •Глава 16. Пример: генерация HTML
- •16.1. HTML
- •16.2. Утилиты HTML
- •16.3. Утилита для итерации
- •16.4. Генерация страниц
- •Глава 17. Пример: объекты
- •17.1. Наследование
- •17.2. Множественное наследование
- •17.3. Определение объектов
- •17.4. Функциональный синтаксис
- •17.5. Определение методов
- •17.6. Экземпляры
- •17.7. Новая реализация
- •17.8. Анализ
- •Комментарии
- •Алфавитный указатель
6
Функции
Понимание функций – один из ключей к пониманию всего Лиспа. Функ ции – основная концепция, которая легла в основу этого языка. На прак тике они являются наиболее полезным инструментом, находящимся в вашем распоряжении.
6.1. Глобальные функции
Предикат fboundp сообщает, существует ли функция с именем, задан ным в виде символа. Если какой-либо символ является именем функ ции, то ее можно получить с помощью symbol-function:
>(fboundp ’+)
T
>(symbol-function ’+) #<Compiled-Function + 17BA4E>
Используя setf над symbol-function:
> (setf (symbol-function ’add2) #’(lambda (x) (+ x 2)))
мы можем определить новую глобальную функцию, которую можно ис пользовать точно так же, как если бы мы определили ее с помощью defun:
> (add2 1) 3
Фактически defun делает немногим более чем просто преобразование выражения типа
(defun add2 (x) (+ x 2))
в выражение с setf. Использование defun делает программы более лег кими для понимания, а также сообщает компилятору некоторую ин
112 |
Глава 6. Функции |
формацию о функции, хотя, строго говоря, использовать defun при на писании программ вовсе не обязательно.
Сделав первым аргументом defun выражение вида (setf f), вы определи те, что будет происходить, когда setf вызван с первым аргументом f.° Ниже приведен пример функции primo, которая является аналогом car:
(defun primo (lst) (car lst))
(defun (setf primo) (val lst) (setf (car lst) val))
В последнем определении на месте имени функции находится выраже ние (setf primo), первым параметром является устанавливаемое значе ние, а вторым – аргумент primo.°
Теперь любой setf для primo будет являться вызовом функции, опреде ленной выше:
> (let ((x (list ’a ’b ’c))) (setf (primo x) 480)
x) (480 B C)
Совсем не обязательно определять саму функцию primo, чтобы опреде лить поведение (setf primo), однако такие определения обычно даются парами.
Поскольку строки в Лиспе – полноценные выражения, ничто не меша ет им находиться внутри тела функции. Сама по себе строка не вызыва ет побочных эффектов, поэтому она ни на что не влияет, если не являет ся последним выражением. Если же строка будет первым выражением, она будет считаться строкой документации к функции:
(defun foo (x)
"Implements an enhanced paradigm of diversity." x)
Документация к функции, определенной глобально, может быть полу чена с помощью documentation:
> (documentation ’foo ’function)
"Implements an enhanced paradigm of diversity."
6.2. Локальные функции
Функции, определенные с помощью defun или setf вместе с symbol-func tion, являются глобальными. Как и глобальные переменные, они могут быть использованы везде. Кроме того, есть возможность определять ло кальные функции, которые, как и локальные переменные, доступны лишь внутри определенного контекста.
Локальные функции могут быть определены с помощью конструкции labels – своего рода let для функций. Ее первым аргументом является не
6.3. Списки параметров |
113 |
список, задающий переменные, а список определений локальных функ ций. Каждый элемент этого списка является списком следующего вида:
(name parameters . body)
Внутри labels любое выражение с name эквивалентно лямбда-вызову
(lambda parameters . body).
> (labels ((add10 (x) + x 10)) (consa (x) (cons ’a x)))
(consa (add10 3))) (A . 13)
Аналогия с let, однако, не работает в одном случае. Локальная функ ция в labels может ссылаться на любые другие функции, определенные в той же конструкции labels, в том числе и на саму себя. Это позволяет создавать с помощью labels локальные рекурсивные функции:
> (labels ((len (lst)
(if (null lst) 0
(+ (len (cdr lst)) 1)))) (len ’(a b c)))
3
В разделе 5.2 говорилось о том, что конструкция let может рассматри ваться как вызов функции. Аналогично выражение с do может считать ся вызовом рекурсивной функции. Выражение:
(do ((x a (b x)) (y c (d y)))
((test x y) (z x y)) (f x y))
эквивалентно
(labels ((rec (x y)
(cond ((test x y) (z x y)) (t
(f x y)
(rec (b x) (d y))))))
(rec a c))
Эта модель может служить для разрешения любых вопросов, которые у вас еще могли остаться касательно поведения do.
6.3.Списки параметров
Вразделе 2.1 было показано, что благодаря префиксной нотации + мо жет принимать любое количество аргументов. С тех пор мы познакоми лись с многими подобными функциями. Чтобы написать такую функ цию самостоятельно, нам придется воспользоваться остаточным па раметром (rest).
114 |
Глава 6. Функции |
Если мы поместим элемент &rest перед последней переменной в списке параметров функции, то при вызове этой функции последний параметр получит список всех оставшихся аргументов. Теперь мы можем напи сать funcall с помощью apply:
(defun our-funcall (fn &rest args) (apply fn args))
Также нам уже знакомы параметры, которые могут быть пропущены и в таком случае получат значение по умолчанию. Такие параметры на зываются необязательными (optional). (Обычные параметры иногда на зываются обязательными (required).) Если символ &optional встречает ся в списке параметров функции
(defun philosoph (thing &optional property) (list thing ’is property))
то все последующие аргументы будут необязательными, а их значением по умолчанию будет nil:
> (philosoph ’death) (DEATH IS NIL)
Мы можем задать исходное значение явно, заключив его в скобки вме сте с параметром. Следующая версия philosoph
(defun philosoph (thing &optional (property ’fun)) (list thing ’is property))
имеет более «радостное» значение по умолчанию:
> (philosoph ’death) (DEATH IS FUN)
Значением по умолчанию необязательного параметра может быть не только константа, но и любое Лисп-выражение. Оно будет вычисляться заново каждый раз, когда это потребуется.
Параметры по ключу (keyword) предоставляют большую гибкость, чем необязательные параметры. Поместив символ &key в список параметров, вы помечаете все последующие параметры как необязательные. Кроме того, при вызове функции эти параметры будут передаваться не в соот ветствии с их ожидаемым положением, а по соответствующей метке – ключевому слову:
> (defun keylist (a &key x y z) (list a x y z))
KEYLIST
>(keylist 1 :y 2) (1 NIL 2 NIL)
>(keylist 1 :y 3 :x 2) (1 2 3 NIL)
Для параметров по ключу, так же как и для необязательных, значени ем по умолчанию является nil, но в то же время можно задать выраже ние для их вычисления.