- •Оглавление
- •Предисловие
- •Предисловие к русскому изданию
- •Глава 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. Анализ
- •Комментарии
- •Алфавитный указатель
42 |
Глава 2. Добро пожаловать в Лисп |
2.14.Функции как объекты
ВЛиспе функции – это самые обычные объекты, такие же как симво лы, строки или списки. Давая функции имя с помощью function, мы по лучаем ассоциированный объект. Как и quote, function – это специаль ный оператор, и поэтому нам не нужно брать в кавычки его аргумент:
> (function +) #<Compiled-Function + 17BA4E>
Таким странным образом отображаются функции в типичной реализа ции Common Lisp.
До сих пор мы имели дело только с такими объектами, которые при пе чати отображаются так же, как мы их ввели. Это соглашение не рас пространяется на функции. Встроенная функция + обычно является куском машинного кода. В каждой реализации Common Lisp может быть свой способ отображения функций.
Аналогично использованию кавычки вместо quote возможно употреб ление #’ как сокращения для function:
> #’+
#<Compiled-Function + 17BA4E>
Как и любой другой объект, функция может служить аргументом. При мером функции, аргументом которой является функция, является apply. Она требует функцию и список ее аргументов и возвращает результат вызова этой функции с заданными аргументами:
>(apply #’+ ’(1 2 3))
6
>(+ 1 2 3)
6
Apply принимает любое количество аргументов, но последний из них обязательно должен быть списком:
> (apply #’+ 1 2 ’(3 4 5)) 15
Функция funcall делает то же самое, но не требует, чтобы аргументы были упакованы в список:
> (funcall #’+ 1 2 3) 6
Обычно создание функции и определение ее имени осуществляется с помощью макроса defun. Но функция не обязательно должна иметь имя, и для ее определения мы не обязаны использовать defun. Подобно большинству других объектов Лиспа, мы можем задавать функции буквально.
Чтобы буквально сослаться на число, мы используем последовательность цифр. Чтобы таким же образом сослаться на функцию, мы используем
2.14. Функции как объекты |
43 |
лямбда-выражение. Лямбда-выражение – это список, содержащий сим вол lambda и следующие за ним список аргументов и тело, состоящее из 0 или более выражений. Ниже приведено лямбда-выражение, пред ставляющее функцию, которая складывает два числа и возвращает их сумму:
(lambda (x y) (+ x y))
Список (x y) содержит параметры, за ним следует тело функции.
Лямбда-выражение можно считать именем функции. Как и обычное имя функции, лямбда-выражение может быть первым элементом вызо ва функции:
>((lambda (x) (+ x 100)) 1) 101
адобавляя #’ к этому выражению, можно получить соответствующую функцию:
>(funcall #’(lambda (x) (+ x 100))
1)
101
Помимо прочего, такая запись позволяет использовать функции, не при сваивая им имена.
Что такое Лямбда?
Влямбда-выражении lambda не является оператором. Это просто символ.° В ранних диалектах Лиспа он имел свою цель: функции имели внутреннее представление в виде списков, и единственным способом отличить функцию от обычного списка была проверка того, является ли первый его элемент символом lambda.
ВCommon Lisp вы можете задать функцию в виде списка, но они будут иметь отличное от списка внутреннее представление, по этому lambda больше не требуется. Было бы вполне возможно за писывать функции, например, так:
((x) (+ x 100))
вместо
(lambda (x) (+ x 100))
но Лисп-программисты привыкли начинать функции символом lambda, и Common Lisp также следует этой традиции.
44 |
Глава 2. Добро пожаловать в Лисп |
2.15. Типы
Лисп обладает необыкновенно гибкой системой типов. Во многих язы ках необходимо задать конкретный тип для каждой переменной перед ее использованием. В Common Lisp значения имеют типы, а перемен ные – нет. Представьте, что у каждого объекта есть метка, которая опре деляет его тип. Такой подход называется динамической типизацией. Вам не нужно объявлять типы переменных, поскольку любая перемен ная может содержать объект любого типа.
Несмотря на то, что объявления типов вовсе не обязательны, вы можете задавать их из соображений производительности. Объявления типов обсуждаются в разделе 13.3.
В Common Lisp встроенные типы образуют иерархию подтипов и надти пов. Объект всегда имеет несколько типов. Например, число 27 соответ ствует типам fixnum, integer, rational, real, number, atom и t в порядке уве личения общности. (Численные типы обсуждаются в главе 9.) Тип t яв ляется самым верхним во всей иерархии, поэтому каждый объект име ет тип t.
Функция typep определяет, принадлежит ли ее первый аргумент к ти пу, который задается вторым аргументом:
> (typep 27 ’integer) T
По мере изучения Common Lisp мы познакомимся с самыми разными встроенными типами.
2.16.Заглядывая вперед
Вэтой главе мы довольно поверхностно познакомились с Лиспом. Но все же начинает вырисовываться портрет этого довольно необычного язы ка. Начнем с того, что он имеет единый синтаксис для записи всего ко да. Синтаксис основан на списках, которые являются разновидностью объектов Лиспа. Функции, которые также являются Лисп-объектами, могут быть представлены в виде списков. А сам Лисп – это тоже про грамма на Лиспе, почти полностью состоящая из встроенных Лиспфункций, которые не отличаются от тех функций, которые вы можете определить самостоятельно.
Не смущайтесь, если эта цепочка взаимоотношений пока вам не совсем ясна. Лисп содержит настолько много новых идей, что их осознание требует времени. Но одно должно быть ясно: некоторые из этих идей из начально довольно элегантны.
Ричард Гэбриэл однажды в полушуточной форме назвал С языком для написания UNIX.° Также и мы можем назвать Лисп языком для напи сания Лиспа. Но между этими утверждениями есть существенная раз ница. Язык, который легко написать на самом себе, кардинально отли
Итоги главы |
45 |
чается от языка, годного для написания каких-то отдельных классов программ. Он открывает новый способ программирования: вы можете как писать программы на таком языке, так и совершенствовать язык в соответствии с требованиями своих программ. Если вы хотите понять суть программирования на Лиспе, начните с этой идеи.
Итоги главы
1.Лисп интерактивен. Вводя выражение в toplevel, вы тут же получае те его значение.
2.Программы на Лиспе состоят из выражений. Выражение может быть атомом или списком, состоящим из оператора и следующих за ним аргументов. Благодаря префиксной записи этих аргументов может быть сколько угодно.
3.Порядок вычисления вызовов функций в Common Lisp таков: снача ла вычисляются аргументы слева направо, затем они передаются оператору. Оператор quote не подчиняется этому порядку и возвра щает само выражение неизменным (вместо его значения) .
4.Помимо привычных типов данных в Лиспе есть символы и списки. Программы на Лиспе записываются в виде списков, поэтому легко составлять программы, которые пишут другие программы.
5.Есть три основные функции для обращения со списками: cons, строя щая список; car, возвращающая первый элемент списка; cdr, возвра щающая весь список, за исключением его первого элемента.
6.В Common Lisp символ t имеет истинное значение, nil – ложное. В контексте логических операций все, кроме nil, является истин ным. Основной условный оператор – if. Операторы and и or также мо гут считаться условными.
7.Лисп состоит по большей части из функций. Собственные функции можно создавать с помощью defun.
8.Функция, которая вызывает сама себя, называется рекурсивной. Рекурсивную функцию следует понимать как процесс, но не как аг регат.
9.Скобки не создают затруднений, поскольку программисты пишут и читают Лисп-код по отступам.
10.Основные функции ввода-вывода: read, являющаяся полноценным обработчиком Лисп-выражений, и format, использующая для вывода заданный шаблон.
11.Локальные переменные создаются с помощью let, глобальные – с по мощью defparameter.
12.Для присваивания используется оператор setf. Его первый аргумент может быть как переменной, так и выражением.