forked from rigidus/onlisp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
00.00-Introduction.txt
310 lines (268 loc) · 28.8 KB
/
00.00-Introduction.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
Предисловие
Эта книга предназначена для каждого, кто хочет улучшить свои навыки
программирования на Лисп. Подразумевается наличие некоторого
знакомства читателя с Лисп, но наличие большого опыта программирования
не требуется. Первые несколько глав содержат развернутое
введение. Надеюсь, что они также будут интересны и более опытным
программистам на Лисп, поскольку представляют знакомые темы в
новом свете.
Довольно трудно выразить сущность языка программирования в одном
предложении, но Джон Фодераро подобрался к этому вплотную:
Лисп – это программируемый язык программирования.
На самом деле Лисп это много большее, но способность подстроить под
себя Лисп в значительной степени отличает профессионала от
новичка. Опытные программисты на Лисп не только приспосабливают
программы к языку, но и создают язык для нужд своих программ. Эта
книга учит, как программировать в восходящем стиле,
для которого Лисп, по сути, хорошо подходит.
Восходящее проектирование
По мере возрастания сложности программного обеспечения восходящее
проектирование становится все более значимым. Сегодня программы могут
иметь чрезвычайно сложные или незавершенные спецификации. При подобных
обстоятельствах традиционное нисходящее проектирование иногда дает
сбой. В этом случае как раз и применяется стиль программирования,
совершенно отличающийся от того, что сейчас изучают в большинстве
курсов программирования: стиль <<снизу-вверх>>, согласно которому
программы пишутся в виде последовательности слоев, каждый из которых
выступает в роли своего рода языка программирования для вышестоящего
слоя. Например, X Windows и TeX – программы, написанные в этом стиле.
Основными темами книги являются следующие две: то, что Лисп – это
естественный язык для программ, написанных в восходящем стиле, а также
то, что восходящий стиль является естественным для написания программ
на Лисп. Поэтому, книга <<К вопросу о Лисп>> будет интересна следующим
двум категориям читателей. Тем, кто заинтересован в написании
расширяемых програм, эта книга покажет, что вы сможете сделать,
используя правильный язык. Лисп-программистам книга даст практическое
объяснение того, как использовать Лисп с наибольшей отдачей.
Название раздела подчеркивает важность метода программирования
снизу-вверх в языке Лисп. Вместо того, чтобы просто написать свою
программу на Лисп, можно реализовать собственный язык на Лисп, и
написать на нем свою программу.
Писать программы в восходящем стиле можно при помощи любого языка, но
Лисп является наиболее естественной движущей силой для такого стиля
программированя. В Лисп восходящее проектирование не является какой-то
особой техникой, применяемой исключительно для больших или сложных
задач. Любая достаточно солидная программа будет отчасти написана в
таком стиле. Лисп с самого начала был задуман как расширяемый
язык. Сам по себе он, в большой степени, просто коллекция
Лисп-функций, мало отличающихся от тех что вы определяете сами. И что
более важно - функции Лисп можно рассматривать как списки, которые в
свою очередь являются структурами данных Лисп. А это значит что можно
писать Лисп-функции, которые генерируют код на Лисп.
Хороший программист на Лисп должен понимать, как извлечь выгоду из этой
возможности. Для этого обычно определяют специального вида операторы,
называемые макросами. Овладение макросами – одна из важнейших ступеней
на пути от написания корректных программ на Лисп до создания
прекрасных программ. Обычно в книгах, знакомящих читателя с Лисп,
хватает места не более чем на небольшой обзор макросов. Читателю лишь
объясняют, что такое макросы, и показывают пару примеров, намекающих
на странные и удивительные вещи, к которым они открывают доступ. Этим
странным и удивительным вещам уделяется особое внимание в этой
книге. Одна из её целей состоит в том, чтобы собрать вместе
все знания и опыт по работе с макросами.
Разумеется, вводные книги о Лисп не уделяют должного внимания отличию
Лисп от других языков. Их задача – донести знание до студентов,
которые, в большинстве своем, привыкли думать о программировании в
терминах языка Паскаль. Такой подход больше запутывает, чем
объясняет: хотя defun и похож на объявление процедуры, он представляет
собой программу для создания программ, генерирующую код, который
построит функциональный объект и проиндексирует его под символом,
переданным в качестве первого аргумента.
Объяснить отличия Лисп от других языков – так же одна из целей этой
книги. В начале было известно то, что при прочих равных, я бы стал
писать программы на Лисп нежели на Си, Паскале или Фортране. Так же
было известно, что это не только и не столько дело вкуса. Но так же
было ясно, что если я и правда соберусь сказать что Лисп это лучший
язык в том или ином роде, то лучше быть готовым объяснить почему.
Когда кто-то спросил Луи Армстронга о том, что такое джаз, он ответил:
«Если вы спрашиваете, что такое джаз, то этого никогда не узнаете». Но
он дал ответ иным способом: он показал людям, что такое джаз. Один из
способов объяснить сильные стороны Лисп – это показать техники, которые
будет сложно или невозможно реализовать в других языках. Большинство
книг по программированию, даже книги по программированию на Лисп,
рассматривают класс таких программ, которые могут быть написаны на
любом языке. В книге <<К вопросу о Лисп>> рассматриваются программы,
которые можно написать только на Лисп. Расширяемость, восходящий стиль
программирования, интерактивная разработка, преобразование исходного
кода, встраиваемые языки – это области, где Лисп показывает свое
преимущество.
Конечно, в принципе любой Тьюринг-эквивалентный язык программирования
способен делать то же самое, что и любой другой. Но это – не главная
особенность языков программирования. Теоретически, все, что возможно
сделать с помощью языка программирования, возможно сделать также и с
помощью машины Тьюринга; на практике-же, программирование машины
Тьюринга не стоит затраченных усилий.
Итак, говоря, что эта книга о том как сделать то, что невозможно в
других языках, я не имею в виду «невозможно» в математическом смысле,
а в том который имеет значение для языков программирования. То есть,
если потребуется написать несколько программ из этой книги на C, можно
– было бы это сделать, написав сначала компилятор Лисп. Например,
встраивание Пролог в Си – вы можете представить себе возможный объем
работы? Глава 24 показывает, как сделать это в 180 строк на Лисп.
Тем не менее, была надежда сделать больше, чем просто показать силу
Лисп. Мне также хотелось объяснить, почему Лисп иной. Это оказалось
довольно сложным вопросом – слишком сложным, чтобы можно было ответить
фразой вроде «символьные вычисления». Все, что мне было известно, я
попытался изложить настолько ясно, насколько смог.
План книги
Так как функции – это основа Лисп-программ, книга начинается с
нескольких глав посвящённых функциям. Глава 2 объясняет, что такое
функции в Лисп, а также раскрывает предоставляемые ими
возможности. Затем, в главе 3 рассматриваются преимущества
функционального программирования – основного стиля программ на
Лисп. Глава 4 показывает как использовать функции для расширения
Лисп. Далее, в главе 5 представлены новые виды абстракций, которые
могут быть определены при помощи функций, возвращающих другие
функции. И, наконец, глава 6 показывает, как использовать функции
вместо традиционных структур данных.
Оставшаяся часть книги более посвящена макросам, нежели
функциям. Макросам уделяется больше внимания отчасти потому, что о них
можно говорить больше, и отчасти потому, что до настоящего момента им
не уделялось достаточно внимания в печати. Главы 7-10 представляют
собой полное учебное пособие по технике макросов. К концу вы узнаете
большую часть того, что опытный Лисп-программист знает о макросах: как
они работают; как их определять, тестировать и отлаживать; когда
использовать макросы, а когда – нет; основные типы макросов; как
писать программы, генерирующие макрорасширения; основные отличия стиля
макросов от стиля Лисп; а также, как диагностировать и исправить любую
проблему из тех, которым подвержены макросы.
Главы 11-18, следующие за этим учебным пособием, показывают кое-что из
сложных абстракций, которые можно создать, применяя макросы. Глава 11
показывает, как написать классический макрос – тот, который создает
контекст, реализует циклы или условия. Глава 12 объясняет роль
макросов в операциях с обобщенными переменными. Глава 13 показывает,
как макросы позволяют программам работать быстрее за счет переноса
вычислений на время компиляции. Глава 14 знакомит с анаморфными
макросами, которые позволяют использовать местоимения в
программах. Глава 15 демонстрирует, как предоставить более удобный
интерфейс функциям-конструкторам, определенным в главе 5. Глава 16
показывает, как заставить Лисп писать программы, используя
макроопределяемые макросы. Глава 17 рассматривает макросы чтения, а
глава 18 — макросы-деструкторы.
Глава 19 открывает четвертую часть книги, посвященную встраиваемым
языкам. Она знакомит с темой, реализуя одну и ту же программу для
ответа на запросы к базе данных, сначала как интерпретатор, а затем
как полноценный встраиваемый язык. Глава 20 демонстрирует, как
включить в программы на Коммон Лисп механизм «продолжений»,
представления объектов, как остатка вычислений. Механизм продолжений –
очень мощный иструмент, который можно использовать как для реализации
многопроцессности, так и для недетерминированного выбора. Встраивания этих
управляющих структур в Лисп рассматриваются в главах 21 и 22
соответственно. Недетерминизм, который позволяет писать програмы так,
будто они способны предвидеть, звучит как демонстрация
невиданной силы. Главы 23 и 24 представляют два встраиваемых языка,
показывающих что недетрменизм отлично себя оправдывает: полный
синтаксический анализатор ATN (augmented transition network -
расширенная сеть переходов) и встраиваемый Пролог, включающие в себя
общей сложностью около 200 строчек кода.
Сам по себе факт того, что эти программы короткие, ничего не
значит. Если прибегнуть к написанию малопонятного кода, неизвестно,
что можно сделать в 200 строчках. Идея в том, что они короткие не
потому, что зависят от программистских трюков, а потому, что они
написаны на Лисп правильным образом. Цель глав 23 и 24 состоит не в
том, как реализовать ATN в одну страницу кода, а Пролог – в две, а в
том, чтобы показать, что эти программы, наиболее естественным образом
реализованные на Лисп, просто такие короткие. Встраиваемые языки в
последних главах доказывают примером те два утвержения, с которых я
начал: Лисп – это естественный язык для разработки снизу-вверх, и
восходящий стиль – естественный способ использования Лисп.
Книга завершается рассмотрением объектно-ориентированного
программирования, в частности CLOS (Common Lisp Object System -
объектная система Коммон Лисп). Оставив эту тему напоследок, мы увидим
более ясно, что объектно-ориентированное программирование является
расширением идей, уже представленных в Лисп. Это одна из множества
абстракций, которые могут быть реализованы поверх Лисп.
Примечания к главам начинаются на странице 387. Примечания содержат
ссылки, дополнительный или альтернативный код, или описания
особенностей лисп, напрямую не связанные с предметом
обсуждения. Примечания выделены маленьким кружком на полях, как
этот. Там также есть приложение (страница 381) о модулях.
Точно так же, как экскурсия по Нью-Йорку может быть экскурсией по
большинству мировых культур, так и изучение Лисп, как программируемого
языка программирования, охватывает большую часть технических приемов
Лисп. Большая часть приемов, описанных здесь, общеизвестна
Лисп-сообществу, но многие до настоящего момента так и не были нигде
опубликованы. А некоторые вопросы, такие как роль макросов, или
сущность захвата переменных, лишь смутно понятны даже многим опытным
Лисп-программистам.
Примеры
Лисп – это семейство языков. Так как Коммон Лисп продолжает оставаться
широко используемым диалектом, большинство примеров в этой книге
реализованы на нем. Язык изначально был сформулирован 1984 в
публикации Гая Стила (Guy Steel) “Common Lisp: the Language”
(CLTL1). Это описание было заменено в 1990 году публикацией нового
издания (CLTL2), которое в свою очередь уступит место грядущему
стандарту ANSI.
Эта книга содержит сотни примеров, начиная небольшими выражениями, и
заканчивая работающей реализацией Пролога. Код в этой книге везде, где
это возможно, писался для работы под любой версией Коммон Лисп. Те
несколько примеров, которым требуются функциональность, не
содержащаяся в реализациях CLTL1, явно выделены в тексте. Последние
главы содержат несколько примеров на языке Схема. Они также явно
выделены.
Код доступен через анонимный FTP с сервера endor.harvard.edu, в
директории pub/onlisp. Вопросы и комментарии можно отправлять по
адресу [email protected].
Благодарности
Я особенно благодарен Роберту Моррису (Robert Morris) за оказанную
помощь во время работы над этой книгой. Я обращался к нему постоянно
за советами, и всегда был этому рад. Несколько примеров в этой книге
заимствованы из кода, изначально написанного им, включая версию for на
странице 127, версию aand на странице 191, match на странице 239,
true-choose алгоритма поиска в ширину на странице 304, и интерпретатор
Prolog в разделе 24.2. На самом деле, вся книга отражает (а временами
даже пересказывает) мои беседы с Робертом за последние семь
лет. (Спасибо, читайте руководство FIXME!)
Хотелось бы отдельно выразить благодарность Дэвиду Муну (David Moon),
он очень внимательно прочитал большие части рукописи и дал очень
ценные комментарии. Глава 12 была полностью переписана по его совету,
а также пример захвата переменной на странице 199 был предоставлен им.
Мне повезло, что техническими рецинзентами книги были Дэвид Турецки
(David Touretzky) и Скона Бриттен (Skona Brittain). Несколько разделов
были добавлены или переписаны по их совету. Альтернативный настоящий
недетерминированный оператор выбора на странице 397 основан на совете,
данным Дэвидом Турецки.
Еще нескольких человек, кто согласился прочитать всю рукопись целиком
или ее часть, включая Тома Читхэма (Tom Cheatham), Ричарда Дрэйвса
(Richard Draves) (он также переписал alambda и propmacro в 1985
FIXME), Джона Фодераро (John Foderaro), Дэвида Хендлера (David
Hendler), Джорджа Люгера (George Luger), Роберта Мюллера (Robert
Muller), Марка Ницберга (Mark Nitzberg) и Гая Стила (Guy Steele).
Я признателен профессору Читхэму, и Гарвардскому университету вообще,
за предоставленные средства, использованные при написании этой
книги. Также выражаю благодарность сотрудникам лаборатории Эйкена,
включая Тони Хартмана (Tony Hartman), Януша Джуду (Janusz Juda), Гарри
Вочнера (Harry Bochner) и Джоанн Клес (Joanne Klys).
Все люди в Prentice Hall проделали отличную работу. Мне
посчастливилось работать с Аланом Аптом (Alan Apt), отличным
редактором и отличным парнем. Благодарю также Мону Помпили (Mona
Pompili), Ширли Майклс (Shirley Michaels) и Ширли Макгир (Shirley
McGuire) за их организованность и хороший юмор.
Несравненного Джино Ли (Gino Lee) из издательства Bow and Arrow,
Кембридж, сделавшего обложку. Дерево на обложке упоминается, в
частности, на странице 27.
Эта книга была набрана с использованием LATEX, языка, написанного
Лесли Лэмпортом (Leslie Lamport) поверх TEX Дональда Кнута (Donald
Knuth) с дополнительными макросами Л. А. Карра (L. A. Carr), Вана
Джэкобсона (Van Jacobson) и Гая Стила (Guy Steele). Диаграммы были
выполнены при помощи Idraw Джона Влиссидеса (John Vlissides) и Скотта
Стэнтона (Scott Stanton). Текст целиком просматривался с помощью
Ghostview Тима Тэйсена (Tim Theisen), который основан на Ghostscript
Л. Питера Дойча (L. Peter Deutsch). Гэри Бисби (Gary Bisbee) из Chiron
Inc., изготовившего оригинал-макет.
Я также в долгу у многих других, включая Пола Беккера (Paul Becker),
Фила Чапника (Phil Chapnick), Элис Хартли (Alice Hartley), Гленна
Холловея (Glenn Holloway), Мейчун Хсу (Meichun Hsu), Крзыштова Ленка
(Krzysztof Lenk), Армана Магболе (Arman Maghbouleh), Говарда Муллингса
(Howard Mullings), Нэнси Пармет (Nancy Parmet), Роберта Пенни (Robert
Penny), Гэри Сабота(Gary Sabot), Патрика Слэйни (Patrick Slaney),
Стива Страссмана (Steve Strassman), Дэйва Уоткинса (Dave Watkins),
Вейкеров (Weickers), и Билла Вудса (Bill Woods).
Больше всего благодарю моих родителей за их пример и поддержку, и
Джеки (Jackie), научившей меня тому, что я должен был бы усвоить,
слушая их.
Надеюсь, что чтение этой книги будет развлечением. Из всех языков,
которые я знаю, мне больше всего нравится Lisp просто потому, что он
наиболее красивый. Эта книга о Lisp, и она сама лисповая. Мне было
очень радостно ее писать, и я надеюсь, что это пройдет сквозь текст.
Пол Грэм