- •Оглавление
- •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.Практикум: Простая база данных
Очевидно, перед тем, как создавать настоящие программы на Lisp, вам необходимо изучить язык. Но давайте смотреть правде в глаза — вы можете подумать "Practical Common Lisp"? Не оксюморон ли это? Зачем тратить силы на изучение деталей языка, если на нем невозможно сделать что-то дельное?". Итак, для начала я приведу маленький пример того, что можно сделать с помощью Common Lisp. В этой главе вы напишете простую базу данных для организации коллекции CD. В главе 27 вы будете использовать схожую технику при создании базы данных записей в формате MP3 для вашего потокового MP3-сервера. Фактически, можете считать это частью вашего программного проекта — в конце концов, для того, чтобы иметь сколько-нибудь MP3-записей для прослушивания, совсем не помешает знать, какие записи у вас есть, а какие нужно извлечь с диска.
В этой главе я пройдусь по языку Lisp достаточно для того, чтобы вы продвинулись до понимания того, каким образом работает код на нём. Но я не буду вдаваться в детали. Вы можете не беспокоиться, если что-то здесь будет вам непонятно — в нескольких следующих главах все используемые здесь (а также многие другие) конструкции Common Lisp будут описаны гораздо более систематически.
Одно замечание по терминологии: в этой главе я расскажу о некоторых операторах Lisp. В главе 4 вы узнаете, что Common Lisp предоставляет три разных типа операторов: функции, макросы и операторы специального назначения. Для целей этой главы вам необязательно понимать разницу. Однако я буду ссылаться на различные операторы как на функции, макросы или специальные операторы, в зависимости от того, чем они на самом деле являются, вместо того, чтобы попытаться скрыть эти детали за одним словом — оператор. Сейчас вы можете рассматривать функции, макросы и специальные операторы как более или менее эквивалентные сущности1).
Также имейте ввиду, что я не буду использовать все наиболее сложные техники Common Lisp для вашей первой после "Hello, world" программы. Цель этой главы не в том, чтобы показать, как вам следует писать базу данных на Lisp; скорее, цель в том, чтобы вы получили представление, на что похоже программирование на Lisp и видение того, что даже относительно простая программа на Lisp может иметь много возможностей.
3.1.Cd и Записи
Чтобы отслеживать диски, которые нужно перекодировать в MP3, и знать, какие из них должны быть перекодированы в первую очередь, каждая запись в базе данных будет содержать название и имя исполнителя компакт диска, оценку того, насколько он нравится пользователю и флаг, указывающий, был ли диск уже перекодирован. Итак, для начала вам необходим способ представления одной записи в базе данных (другими словами, одного CD). Common Lisp предоставляет для этого много различных структур данных, от простого четырехэлементного списка до определяемого пользователем с помощью CLOS класса данных.
Для начала вы можете остановиться на простом варианте и использовать список. Вы можете создать его с помощью функции LIST, которая, соответственно, возвращает список 2) из переданных аргументов.
CL-USER> (list 1 2 3) (1 2 3)
Вы могли бы использовать четырёхэлементный список, отображающий позицию в списке на соответствующее поле записи. Однако другая существующая разновидность списков, называемая property list (список свойств) или, сокращенно, plist, в нашем случае гораздо удобнее. Plist — это такой список, в котором каждый нечетный элемент является символом, описывающим следующий (чётный) элемент списка. На этом этапе я не буду углубляться в подробности понятия символ; по своей природе это имя. Для символов, именующих поля в базе данных, мы можем использовать частный случай символов, называемый символами-ключами (keyword symbol). Ключ — это имя, начинающееся с двоеточия (:), например, :foo 3). Вот пример plist, использующего символы-ключи :a, :b и :c как имена свойств:
CL-USER> (list :a 1 :b 2 :c 3) (:A 1 :B 2 :C 3)
Заметьте, вы можете создать список свойств той же функцией LIST, которой создавали прочие списки. Характер содержимого — вот что делает его списком свойств.
Причина, по которой использование plist является предпочтительным — наличие функции GETF, в которую передают plist и желаемый символ и получают следующее за символом значение. Это делает plist чем-то вроде упрощенной хэш-таблицы. В Lisp есть и "настоящие" хэш-таблицы, но для ваших текущих нужд достаточно plist, к тому же намного проще сохранять данные в такой форме в файл, это сильно пригодится позже.
CL-USER> (getf (list :a 1 :b 2 :c 3) :a) 1 CL-USER> (getf (list :a 1 :b 2 :c 3) :c) 3
Теперь, зная это, вам будет достаточно просто написать функцию make-cd, которая получит четыре поля в качестве аргументов и вернёт plist, представляющий CD.
(defun make-cd (title artist rating ripped) (list :title title :artist artist :rating rating :ripped ripped))
Слово DEFUN говорит нам,4) что эта запись определяет новую функцию. Имя функции — make-cd. После имени следует список параметров. Функция содержит четыре параметра — title, artist, rating и ripped. Всё, что следует за списком параметров — тело функции. В данном случае тело — лишь форма, просто вызов функции LIST. При вызове make-сd параметры, переданные при вызове, будут связаны с переменными в списке параметров из объявления функции. Например, для создания записи о CD Roses от Kathy Mattea вы можете вызвать make-cd примерно так:
CL-USER> (make-cd "Roses" "Kathy Mattea" 7 t) (:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T)
