- •Оглавление
- •Предисловие
- •Предисловие к русскому изданию
- •Глава 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. Анализ
- •Комментарии
- •Алфавитный указатель
250 |
Глава 14. Более сложные вопросы |
как те, что изображены на рис. 14.2, он может сделать код более доступ ным для понимания.
(defun most (fn lst) (if (null lst)
(values nil nil)
(loop with wins = (car lst)
with max = (funcall fn wins) for obj in (cdr lst)
for score = (funcall fn obj) when (> score max)
do (setf wins obj max score)
finally (return (values wins max)))))
(defun num-year (n) (if (< n 0)
(loop for y downfrom (- yzero 1) until (<= d n)
sum (- (year-days y)) into d
finally (return (values (+ y 1) (- n d)))) (loop with prev = 0
for y from yzero until (> d n)
do (setf prev d)
sum (year-days y) into d finally (return (values (- y 1)
(- n prev))))))
Рис. 14.2. Итерация с помощью loop
14.6.Особые условия
ВCommon Lisp особые условия (conditions) включают ошибки и другие ситуации, которые могут возникать в процессе выполнения. Когда сиг нализируется какое-либо условие, вызывается соответствующий обра ботчик. Обработчик по умолчанию для условий-ошибок обычно вызыва ет цикл прерывания. Но Common Lisp предоставляет и разнообразные операторы для сигнализации и обработки условий. Вы можете переопре делять уже существующие обработчики и даже писать собственные.
Большинству программистов не придется работать с особыми условия ми непосредственно. Однако есть несколько уровней более абстрактных операторов, которые используют условия, поэтому для их понимания все же полезно иметь представление о механизме, изложенном ниже.
14.6. Особые условия |
251 |
В Common Lisp есть несколько операторов для сигнализации об ошиб ках. Основным является error. Один из способов его вызова – передача ему тех же аргументов, что и format:
> (error "Your report uses ~A as a verb." ’status) Error: Your report uses STATUS as verb.
Options: :abort, :backtrace
>>
Если такое условие не обрабатывается, выполнение будет прервано, как показано выше.
Более абстрактными операторами для сигнализации ошибок являются ecase, check-type и assert. Первый напоминает case, но сигнализирует об ошибке, когда не найдено совпадение ни с одним ключом:
> (ecase 1 (2 3) (4 5)) Error: No applicable clause.
Options: :abort, :backtrace
>>
Обычное case-выражение в этом случае вернет nil, но, поскольку счита ется плохим стилем пользоваться этим возвращаемым значением, име ет смысл применить ecase, если у вас нет варианта otherwise.
Макрос check-type принимает место, имя типа и необязательный аргу мент-строку. Он сигнализирует о корректируемой ошибке, если значе ние по данному месту не соответствует заданному типу. Обработчик корректируемой ошибки предложит один из вариантов ее исправления:
> (let ((x ’(a b c)))
(check-type (car x) integer "an integer") x)
Error: The value of (CAR X), A, should be an integer. Options: :abort, :backtrace, :continue
>> :continue
New value of (CAR X)? 99 (99 B C)
>
Только что мы привели пример коррекции ошибки, исправив значение (car x), после чего выполнение продолжилось с исправленным значени ем, как будто оно было передано таким изначально.
Этот макрос был определен с помощью более общего assert. Он прини мает тестовое выражение и список одного или более мест, сопровождае мый теми же аргументами, которые вы бы передали в error:
> (let ((sandwich ’(ham on rye)))
(assert (eql (car sandwich) ’chicken) ((car sandwich))
"I wanted a ~A sandwich." ’chicken) sandwich)
252 Глава 14. Более сложные вопросы
Error: I wanted a CHICHEN sandwich.
Options: :abort, :backtrace, :continue >> :continue
New value of (CAR SANDWICH)? ’chicken (CHICKEN ON RYE)
>
Также имеется возможность создавать собственные обработчики, но этим редко кто пользуется. Обычно обходятся уже имеющимися средст вами, например ignore-errors. Этот макрос ведет себя аналогично progn, если ни один из его аргументов не приводит к ошибке. Если же во время выполнения одного из выражений сигнализируется ошибка, то работа не прерывается, а выражение ignore-errors немедленно завершается с возвратом двух значений: nil и условия, которое было просигнализи ровано.
Например, если вы даете пользователю возможность вводить собствен ные выражения, но не хотите, чтобы синтаксически неверное выраже ние прерывало работу, то вам понадобится защита от некорректно сфор мированных выражений:
(defun user-input (prompt) (format t prompt)
(let ((str (read-line)))
(or (ignore-errors (read-from-string str)) nil)))
Функция вернет nil, если считанное выражение содержит синтаксиче скую ошибку:
> (user-input "Please type an expression> ") Please type an expression> #%@#+!!
NIL