- •Оглавление
- •Предисловие
- •Предисловие к русскому изданию
- •Глава 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. Анализ
- •Комментарии
- •Алфавитный указатель
268 Глава 16. Пример: генерация HTML
> (with-link ’capture
(princ "The Captured Variable"))
<a href="capture.html">The Captured Variable</a> "</a>"
Он используется в функции link-item, которая принимает строку и соз дает элемент списка, также являющийся ссылкой,
>(link-item ’bq "Backquote!") <li><a href="bq.html"> Backquote!</a> "</a>"
ив функции button, которая генерирует ссылку внутри квадратных скобок,
>(button ’help "Help")
[ <a href="help.html">Help</a> ] NIL
так что она напоминает кнопку.
16.3.Утилита для итерации
Вданном разделе мы определим утилиты общего назначения, которые нам понадобятся в дальнейшем. Можем ли мы заранее знать, что в про цессе разработки нам понадобится та или иная утилита? Нет, не можем. Обычно в процессе написания программы вы осознаете необходимость какой-либо утилиты, останавливаетесь, чтобы создать ее, и затем про должаете писать программу, используя эту утилиту. Но описание здесь реального процесса разработки со всеми его остановками и возвратами было бы весьма запутанным. Поэтому мы ограничимся лишь конечным результатом и оговоркой, что написание программ никогда не будет столь прямолинейным, как это описано в книге. Оно всегда будет вклю чать неоднократное полное или частичное переписывание кода.
Наша новая утилита (рис. 16.6) будет разновидностью mapc. Она прини мает функцию трех аргументов и список. Для каждого элемента списка функция применяется как для самого элемента, так и для предшест вующего и последующего элементов. (Если предшествующего или по следующего элемента нет, то вместо них используется nil.)
> (map3 #’(lambda (&rest args) (princ args)) ’(a b c d))
(A NIL B) (B A C) (C B D) (D C NIL) NIL
Как и mapc, она всегда возвращает nil.1 Ситуации, в которых требуется подобная утилита, возникают довольно часто. С одной из них мы столк
1На самом деле, mapc возвращает значение своего второго аргумента. – Прим. перев.
16.4. Генерация страниц |
269 |
немся в следующем разделе, где нам понадобятся ссылки на предыду щую и следующую страницы.
(defun map3 (fn lst)
(labels ((rec (curr prev next left) (funcall fn curr prev next) (when left
(rec (car left) curr
(cadr left) (cdr left)))))
(when lst
(rec (car lst) nil (cadr lst) (cdr lst)))))
Рис. 16.6. Итерация по тройкам
Вариантом общей проблемы может быть случай, когда вам нужно сде лать что-либо между каждой парой элементов списка:
> (map3 #’(lambda (c p n) (princ c)
(if n (princ " | "))) ’(a b c d))
A | B | C | D NIL
Программисты периодически сталкиваются с подобной проблемой – пожалуй, не настолько часто, чтобы иметь встроенный оператор языка для ее решения, но достаточно часто для того, чтобы иметь возмож ность определить такой оператор самостоятельно.
16.4. Генерация страниц
Подобно книгам и журналам, наборы веб-страниц часто имеют древо видную организацию. Книга может состоять из глав, которые содержат разделы, которые, в свою очередь, состоят из подразделов, и т. д. Вебстраницы часто имеют подобную структуру, хотя и не используют по добные термины.
В этом разделе мы напишем программу, генерирующую набор веб-стра ниц, имеющих следующую структуру. Первая страница представляет собой оглавление, содержащее ссылки на разделы, которые, в свою оче редь, содержат ссылки на пункты, являющиеся отдельными страница ми с обычным текстом. Каждая страница должна иметь ссылки для пе ремещения назад, вперед и наверх по древовидной структуре. Ссылки вперед и назад осуществляют переход в пределах одного уровня вложен ности. Например, ссылка вперед на странице, представляющей пункт, будет указывать на следующий пункт данного раздела. По ссылке вверх
270 |
Глава 16. Пример: генерация HTML |
осуществляется переход от пункта к разделу, а от раздела к оглавле нию. Кроме того, должен быть индекс – отдельная страница со ссылка ми, на которой перечисляются все пункты в алфавитном порядке. Опи санная иерархия изображена на рис. 16.7.
section item
contents |
section |
item |
index |
section item
Рис. 16.7. Структура сайта
Операторы и структуры данных, необходимые для создания страниц, представлены на рис. 16.8. Наша программа будет работать с двумя ти пами объектов: пунктами, содержащими блоки текста, и разделами, содержащими списки ссылок на пункты.
(defparameter *sections* nil)
(defstruct item id title text)
(defstruct section id title items)
(defmacro defitem (id title text)
‘(setf ,id |
|
(make-item :id |
’,id |
:title |
,title |
:text |
,text))) |
(defmacro defsection (id title &rest items)
‘(setf ,id |
|
(make-section :id |
’,id |
:title ,title
:items (list ,@items))))
(defun defsite (&rest sections) (setf *sections* sections))
Рис. 16.8. Создание сайта
И разделы, и пункты содержат поле id. В качестве содержимого этого поля будут передаваться символы, имеющие два применения. Первое заключается в определениях defitem и defsection; здесь id – это уникаль ное имя, по которому мы будем ссылаться на пункт или раздел. Второе
16.4. Генерация страниц |
271 |
применение: мы будем использовать id в имени файла, представляюще го пункт или раздел. К примеру, страница, представляющая пункт foo, будет записана в файл "foo.html".
Кроме того, разделы и пункты имеют поля title, которые должны быть строками и будут использоваться в качестве заголовков соответствую щих страниц.
Порядок следования пунктов в разделе определяется из аргументов defsection, порядок следования разделов в оглавлении – из аргументов defsite.
На рис. 16.9. представлены функции, генерирующие оглавление и ин декс. Константы contents и index являются строками, служащими заго ловками этих страниц и именами соответствующих файлов.
(defconstant |
contents |
"contents") |
(defconstant |
index |
"index") |
(defun gen-contents (&optional (sections *sections*)) (page contents contents
(with ol
(dolist (s sections)
(link-item (section-id s) (section-title s)) (brs 2))
(link-item index (string-capitalize index)))))
(defun gen-index (&optional (sections *sections*)) (page index index
(with ol
(dolist (i (all-items sections)) (link-item (item-id i) (item-title i)) (brs 2)))))
(defun all-items (sections) (let ((is nil))
(dolist (s sections)
(dolist (i (section-items s))
(setf is (merge ’list (list i) is #’title<))))
is))
(defun title< (x y)
(string-lessp (item-title x) (item-title y)))
Рис. 16.9. Генерация индекса и оглавления
Функции gen-contents и gen-index в целом похожи друг на друга. Каж дая из них открывает HTML-файл, генерирует заголовок и список ссы лок. Разница лишь в том, что список пунктов в индексе должен быть отсортирован. Он строится с помощью функции all-items, которая ис пользует функцию упорядочения title<. Важно, что все заголовки срав
272 |
Глава 16. Пример: генерация HTML |
ниваются функцией string-lessp, которая, в отличие от string<, игнори рует регистр знаков.
В реальном приложении сравнение может быть более утонченным, на пример, оно может игнорировать начальные артикли «a» и «the».
Оставшийся код показан на рис. 16.10: gen-site генерирует весь набор веб-страниц, остальные функции генерируют разделы и пункты.
(defun gen-site ()
(map3 #’gen-section *sections*) (gen-contents)
(gen-index))
(defun gen-section (sect <sect sect>)
(page (section-id sect) (section-title sect) (with ol
(map3 #’(lambda (item <item item>) (link-item (item-id item)
(item-title item))
(brs 2)
(gen-item sect item <item item>)) (section-items sect)))
(brs 3)
(gen-move-buttons (if <sect (section-id <sect)) contents
(if sect> (section-id sect>)))))
(defun gen-item (sect item <item item>) (page (item-id item) (item-title item)
(princ (item-text item)) (brs 3)
(gen-move-buttons (if <item (item-id <item)) (section-id sect)
(if item> (item-id item>)))))
(defun gen-move-buttons (back up forward) (if back (button back "Back"))
(if up (button up "Up"))
(if forward (button forward "Forward")))
Рис. 16.10. Генерация сайта, разделов и пунктов
Под полным набором страниц понимается оглавление, индекс, страни цы, представляющие каждый из разделов, и страницы, представляю щие каждый из пунктов. Оглавление и индекс создаются с помощью функций, представленных на рис. 16.9. Разделы и пункты генерируют ся gen-section, которая создает страницу раздела и вызывает gen-item для генерации страниц, представляющих каждый пункт в данном раз деле.
16.4. Генерация страниц |
273 |
Эти две функции начинаются и заканчиваются похожим образом: обе принимают аргументы, представляющие сам объект, а также предыду щий и последующий объекты того же уровня вложенности; обе закан чиваются вызовом gen-move-buttons с целью сгенерировать кнопки для перемещения вперед, назад и вверх. Разница в середине: в отличие от gen-section, которая производит упорядоченный список ссылок на пунк ты, gen-item просто отправляет текст в выходной файл.
Каким будет текст каждого из пунктов – личное дело пользователя. Этот текст, к примеру, вполне может содержать HTML-разметку. Или же он может быть сгенерирован другой программой.
Пример ручного создания небольшого набора страниц приведен на рис. 16.11. В нем приводится перечень недавних публикаций в Инсти туте Печенюшек Судьбы.
(defitem des "Fortune Cookies: Dessert or Fraud?" "...") (defitem case "The Case for Pessimism" "...") (defsection position "Position Papers" des case) (defitem luck "Distribution of Bad Luck" "...")
(defitem haz "Health Hazards of Optimism" "...") (defsection abstract "Research Abstracts" luck haz) (defsite position abstract)
Рис. 16.11. Небольшой сайт