- •Оглавление
- •1.Введение
- •2.Знакомство с реализацией Лиспа
- •2.1.Интерактивное программирование
- •2.2.Эксперименты в repl
- •3.Практикум: Простая база данных
- •3.1.Cd и Записи
- •3.2.Заполнение cd
- •3.3.Просмотр содержимого базы данных
- •3.4.Улучшение взаимодействия с пользователем
- •3.5.Сохранение и загрузка базы данных
- •3.6.Выполнение запросов к базе данных
- •3.7.Обновление существующих записей
- •3.8.Избавление от дублирующего кода
- •3.9.Об упаковке
- •4.Синтаксис и семантика
- •4.1.Зачем столько скобок?
- •4.2.Разделение черного ящика
- •4.5.Вызовы функций
- •4.6.Специальные операторы
- •4.7.Макросы
- •4.8.Истина, Ложь и Равенство
- •4.9.Форматирование кода Lisp
- •5.Функции
- •5.1.Определение новых функций
- •5.2.Списки параметров функций
- •5.3.Необязательные параметры
- •5.4.Остаточные (Rest) параметры
- •5.5.Именованные параметры
- •5.6.Совместное использование разных типов параметров
- •5.7.Возврат значений из функции
- •5.8.Функции как данные или Функции высшего порядка
- •5.9.Анонимные функции
- •6. Переменные
- •6.1.Основы переменных
- •6.2.Лексические переменные и замыкания
- •6.3.Динамические (специальные) переменные
- •6.4.Константы
- •6.5.Присваивание
- •6.6.Обобщенное присваивание
- •6.7.Другие способы изменения "мест"
3.9.Об упаковке
Случилась интересная вещь. Вы избавились от дублирования и сделали код одновременно более производительным и универсальным. Так часто бывает, если правильно выбрать макрос. Это имеет смысл, потому что макрос — это ещё один механизм создания абстракций — абстракций на синтаксическом уровне, а абстракции — это, по определению, более короткий путь для выражения подразумеваемых сущностей. Сейчас код мини-базы данных, который относится к CD и полям, его описывающим, находится только в функциях make-cd, prompt-for-cd и add-cd. Фактически, наш новый макрос будет работать с любой базой данных, основанной на списке свойств.
Тем не менее, эта база данных всё еще далека от завершения. Вероятно, вы думаете о добавлении множества возможностей, например, таких, как поддержка множества таблиц или более сложных запросов. В главе 27 мы создадим базу данных о записях MP3, которая будет содержать некоторые из этих возможностей.
Целью этой главы являлось быстрое введение в лишь малую часть возможностей Lisp и демонстрация того, как они используются для написания кода, чуть более интересного, чем "Hello, world". В следующей главе мы начнём более систематический обзор Lisp.
1)Прежде, чем я продолжу, очень важно, чтобы вы забыли все, что можете знать о "макросах" в стиле #define, реализованных в препроцессоре C. Макросы Lisp не имеют с ними ничего общего
2)LIST — по английски СПИСОК. Кстати, последние реализации Common Lisp позволяют писать и на родном для вас языке. Например, на русском можно создать макрос СПИСОК, который будет вызывать LIST, например так:
(defmacro список (&body body) `(list ,@body))
— Прим. перев.
3)foo, bar — любимые имена переменных у англоговорящих программистов, пишущих книги и документацию — Прим. перев.
4)Тем, для кого английский родной — Прим. перев.
5)Использование глобальной переменной имеет ряд недостатков — например, вы можете использовать только одну базу данных в каждый момент времени. В гл. 27, имея за плечами уже солидный багаж знаний о Lisp, вы будете готовы к созданию более гибкой базы данных. В 6 главе вы также увидите, что даже использование глобальных переменных в Common Lisp более гибко, чем это возможно в других языках.
6)Одна из наиболее "классных" управляющих команд FORMAT — команда ~R. Всегда хотели знать, как по-английски произносится действительно большое число? Lisp знает. Сделайте так:
(format nil "~r" 1606938044258990275541962092)
и вы получите (разбито на строки для удобочитаемости): "one octillion six hundred six septillion nine hundred thirty-eight sextillion forty-four quintillion two hundred fifty-eight quadrillion nine hundred ninety trillion two hundred seventy-five billion five hundred forty-one million nine hundred sixty-two thousand ninety-two"
7)На самом деле, Windows позволяет использовать косую черту ("прямые слэши") в именах файлов, хотя в качестве разделителей имён директорий в этой ОС обычно используются обратные косые ("обратные слэши"). Это очень удобно, ведь иначе вам каждый раз приходилось бы дублировать обратную косую черту, так как это — Escape-символ в строках Lisp.
8)Слово lambda используется в Lisp из-за его изначальной связи с лямбда-исчислением, математическим формализмом, изобретенным для изучения математических функций.
9)Техническое обозначение функции, ссылающейся на свободную переменную в своём контексте — замыкание, потому что функция как бы "смыкается" над переменной. Я подробнее расскажу о замыканиях в главе 6.
10)Заметьте, что в Lisp оператор if, как и всё остальное, является выражением, возвращающим значение. Вообще, он больше напоминает тернарный оператор (?:) в Perl, Java и C, поскольку вполне допустимо такое выражение в этих языках:
some_var = some_boolean ? value1 : value2;
А такое - нет:
some_var = if (some_boolean) value1; else value2;
так как в этих языках if — просто оператор, а не выражение.
11)Автор ещё забыл упомянуть макрос WHEN. Он будет подробно рассмотрен в 7-й главе. — прим.ред.
12)Странно, что функция update здесь возвращает NIL, ведь последней операцией является SETF *db* value, которая, в свою очередь, возвращает присвоенное значение. То есть после присваивания можно наблюдать изменения. (sbcl-1.03) — Прим. перев.
13)Вы должны использовать имя delete-rows вместо более очевидного delete, потому что в Common Lisp уже есть функция DELETE. Система пакетов Lisp предоставляет возможность разрешать такие конфликты имён, так что, если хотите, можете иметь в своей программе собственную функцию delete. Но сейчас ещё рано рассказывать вам о пакетах.
14)Если вы беспокоитесь о том, что в этом коде могут возникнуть утечки памяти, будьте уверены: Lisp был первым языком, в котором появилась сборка мусора (и, раз уж на то пошло, использование динамически выделяемой памяти). Память, используемая для старого значения *db*, будет автоматически возвращена системе, как только выяснится, что на неё больше ничто не ссылается.
15)Мой друг однажды проводил собеседование с кандидатом на должность программиста и задал ему обычный вопрос, распространённый на таких собеседованиях: "Как вы понимаете, что функция (или метод) стала слишком велика?" "Ну... — ответил кандидат. — Я стараюсь делать так, чтобы любой метод был меньше, чем моя голова." "Вы хотите сказать, что не можете удержать в голове всех деталей?" — "Нет, я хочу сказать, что сажусь перед монитором, и код не должен быть больше, чем моя голова."
16)Вряд ли проверка того, был ли ключ-параметр передан в функцию, приведёт к существенному падению производительности, так как проверка значения переменной на NIL — очень "дешёвая" с точки зрения производительности операция. С другой стороны, эти функции, возвращаемые where, оказываются как раз в середине внутреннего цикла вызовов select, update и delete-rows, так как вызываются для каждой записи в базе данных. В любом случае, для наглядности, пусть будет так.
17)Макросы также выполняются интерпретатором, — тем не менее, сущность макросов легче понять, когда думаешь о компилируемом коде. Как и обо всём остальном в этой главе, я расскажу об этом более подробно в следующих главах.
