Common Lisp - мультипарадигменный язык программирования общего назначения, подходящий для широкого спектра задач. Его частенько называют программируемым языком программирования.
Идеальная отправная точка - книга Common Lisp на практике (перевод). Ещё одна популярная книга Land of Lisp. И одна из последних книг Common Lisp Recipes вобрала в себя лучшие архитектурные решения на основе опыта коммерческой работки автора.
1;;;-----------------------------------------------------------------------------
2;;; 0. Синтаксис
3;;;-----------------------------------------------------------------------------
4
5;;; Основные формы
6
7;;; Существует два фундамента CL: АТОМ и S-выражение.
8;;; Как правило, сгруппированные S-выражения называют `формами`.
9
1010 ; атом; вычисляется в самого себя
11:thing ; другой атом; вычисляется в символ :thing
12t ; ещё один атом, обозначает `истину` (true)
13(+ 1 2 3 4) ; s-выражение
14'(4 :foo t) ; ещё одно s-выражение
15
16;;; Комментарии
17
18;;; Однострочные комментарии начинаются точкой с запятой. Четыре знака подряд
19;;; используют для комментария всего файла, три для раздела, два для текущего
20;;; определения; один для текущей строки. Например:
21
22;;;; life.lisp
23
24;;; То-сё - пятое-десятое. Оптимизировано для максимального бадабума и ччччч.
25;;; Требуется для функции PoschitatBenzinIsRossiiVBelarus
26
27(defun meaning (life)
28 "Возвращает смысл Жизни"
29 (let ((meh "abc"))
30 ;; Вызывает бадабум
31 (loop :for x :across meh
32 :collect x))) ; сохранить значения в x, и потом вернуть
33
34;;; А вот целый блок комментария можно использовать как угодно.
35;;; Для него используются #| и |#
36
37#| Целый блок комментария, который размазан
38 на несколько строк
39 #|
40 которые могут быть вложенными!
41 |#
42|#
43
44;;; Чем пользоваться
45
46;;; Существует несколько реализаций: и коммерческих, и открытых.
47;;; Все они максимально соответствуют стандарту языка.
48;;; SBCL, например, добротен. А за дополнительными библиотеками
49;;; нужно ходить в Quicklisp
50
51;;; Обычно разработка ведется в текстовом редакторе с запущенным в цикле
52;;; интерпретатором (в CL это Read Eval Print Loop). Этот цикл (REPL)
53;;; позволяет интерактивно выполнять части программы вживую сразу наблюдая
54;;; результат.
55
56;;;-----------------------------------------------------------------------------
57;;; 1. Базовые типы и операторы
58;;;-----------------------------------------------------------------------------
59
60;;; Символы
61
62'foo ; => FOO Символы автоматически приводятся к верхнему регистру.
63
64;;; INTERN создаёт символ из строки.
65
66(intern "AAAA") ; => AAAA
67(intern "aaa") ; => |aaa|
68
69;;; Числа
70
719999999999999999999999 ; целые
72#b111 ; двоичные => 7
73#o111 ; восьмеричные => 73
74#x111 ; шестнадцатиричные => 273
753.14159s0 ; с плавающей точкой
763.14159d0 ; с плавающей точкой с двойной точностью
771/2 ; рациональные)
78#C(1 2) ; комплексные
79
80;;; Вызов функции пишется как s-выражение (f x y z ....), где f это функция,
81;;; x, y, z, ... аругменты.
82
83(+ 1 2) ; => 3
84
85;;; Если вы хотите просто представить код как данные, воспользуйтесь формой QUOTE
86;;; Она не вычисляет аргументы, а возвращает их как есть.
87;;; Она даёт начало метапрограммированию
88
89(quote (+ 1 2)) ; => (+ 1 2)
90(quote a) ; => A
91
92;;; QUOTE можно сокращенно записать знаком '
93
94'(+ 1 2) ; => (+ 1 2)
95'a ; => A
96
97;;; Арифметические операции
98
99(+ 1 1) ; => 2
100(- 8 1) ; => 7
101(* 10 2) ; => 20
102(expt 2 3) ; => 8
103(mod 5 2) ; => 1
104(/ 35 5) ; => 7
105(/ 1 3) ; => 1/3
106(+ #C(1 2) #C(6 -4)) ; => #C(7 -2)
107
108;;; Булевые
109
110t ; истина; любое не-NIL значение `истинно`
111nil ; ложь; а ещё пустой список () тоже `ложь`
112(not nil) ; => T
113(and 0 t) ; => T
114(or 0 nil) ; => 0
115
116;;; Строковые символы
117
118#\A ; => #\A
119#\λ ; => #\GREEK_SMALL_LETTER_LAMDA
120#\u03BB ; => #\GREEK_SMALL_LETTER_LAMDA
121
122;;; Строки это фиксированные массивы символов
123
124"Hello, world!"
125"Тимур \"Каштан\" Бадтрудинов" ; экранировать двойную кавычку обратным слешом
126
127;;; Строки можно соединять
128
129(concatenate 'string "ПРивет, " "мир!") ; => "ПРивет, мир!"
130
131;;; Можно пройтись по строке как по массиву символов
132
133(elt "Apple" 0) ; => #\A
134
135;;; Для форматированного вывода используется FORMAT. Он умеет выводить, как просто значения,
136;;; так и производить циклы и учитывать условия. Первый агрумент указывает куда отправить
137;;; результат. Если NIL, FORMAT вернет результат как строку, если T результат отправиться
138;;; консоль вывода а форма вернет NIL.
139
140(format nil "~A, ~A!" "Привет" "мир") ; => "Привет, мир!"
141(format t "~A, ~A!" "Привет" "мир") ; => NIL
142
143
144;;;-----------------------------------------------------------------------------
145;;; 2. Переменные
146;;;-----------------------------------------------------------------------------
147
148;;; С помощью DEFVAR и DEFPARAMETER вы можете создать глобальную (динамческой видимости)
149;;; переменную.
150;;; Имя переменной может состоять из любых символов кроме: ()",'`;#|\
151
152;;; Разница между DEFVAR и DEFPARAMETER в том, что повторное выполнение DEFVAR
153;;; переменную не поменяет. А вот DEFPARAMETER меняет переменную при каждом вызове.
154
155;;; Обычно глобальные (динамически видимые) переменные содержат звездочки в имени.
156
157(defparameter *some-var* 5)
158*some-var* ; => 5
159
160;;; Можете использовать unicode.
161(defparameter *КУКУ* nil)
162
163;;; Доступ к необъявленной переменной - это непредсказуемое поведение. Не делайте так.
164
165;;; С помощью LET можете сделать локальное связывание.
166;;; В следующем куске кода, `я` связывается с "танцую с тобой" только
167;;; внутри формы (let ...). LET всегда возвращает значение последней формы.
168
169(let ((я "танцую с тобой")) я) ; => "танцую с тобой"
170
171
172;;;-----------------------------------------------------------------------------;
173;;; 3. Структуры и коллекции
174;;;-----------------------------------------------------------------------------;
175
176
177;;; Структуры
178
179(defstruct dog name breed age)
180(defparameter *rover*
181 (make-dog :name "rover"
182 :breed "collie"
183 :age 5))
184*rover* ; => #S(DOG :NAME "rover" :BREED "collie" :AGE 5)
185(dog-p *rover*) ; => T
186(dog-name *rover*) ; => "rover"
187
188;;; DEFSTRUCT автоматически создала DOG-P, MAKE-DOG, и DOG-NAME
189
190
191;;; Пары (cons-ячейки)
192
193;;; CONS создаёт пары. CAR и CDR возвращают начало и конец CONS-пары.
194
195(cons 'SUBJECT 'VERB) ; => '(SUBJECT . VERB)
196(car (cons 'SUBJECT 'VERB)) ; => SUBJECT
197(cdr (cons 'SUBJECT 'VERB)) ; => VERB
198
199
200;;; Списки
201
202;;; Списки это связанные CONS-пары, в конце самой последней из которых стоит NIL
203;;; (или '() ).
204
205(cons 1 (cons 2 (cons 3 nil))) ; => '(1 2 3)
206
207;;; Списки с произвольным количеством элементов удобно создавать с помощью LIST
208
209(list 1 2 3) ; => '(1 2 3)
210
211;;; Если первый аргумент для CONS это атом и второй аргумент список, CONS
212;;; возвращает новую CONS-пару, которая представляет собой список
213
214(cons 4 '(1 2 3)) ; => '(4 1 2 3)
215
216;;; Чтобы объединить списки, используйте APPEND
217
218(append '(1 2) '(3 4)) ; => '(1 2 3 4)
219
220;;; Или CONCATENATE
221
222(concatenate 'list '(1 2) '(3 4)) ; => '(1 2 3 4)
223
224;;; Списки это самый используемый элемент языка. Поэтому с ними можно делать
225;;; многие вещи. Вот несколько примеров:
226
227(mapcar #'1+ '(1 2 3)) ; => '(2 3 4)
228(mapcar #'+ '(1 2 3) '(10 20 30)) ; => '(11 22 33)
229(remove-if-not #'evenp '(1 2 3 4)) ; => '(2 4)
230(every #'evenp '(1 2 3 4)) ; => NIL
231(some #'oddp '(1 2 3 4)) ; => T
232(butlast '(subject verb object)) ; => (SUBJECT VERB)
233
234
235;;; Вектора
236
237;;; Вектора заданные прямо в коде - это массивы с фиксированной длинной.
238
239#(1 2 3) ; => #(1 2 3)
240
241;;; Для соединения векторов используйте CONCATENATE
242
243(concatenate 'vector #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)
244
245
246;;; Массивы
247
248;;; И вектора и строки это подмножества массивов.
249
250;;; Двухмерные массивы
251
252(make-array (list 2 2)) ; => #2A((0 0) (0 0))
253(make-array '(2 2)) ; => #2A((0 0) (0 0))
254(make-array (list 2 2 2)) ; => #3A(((0 0) (0 0)) ((0 0) (0 0)))
255
256;;; Внимание: значение по-умолчанию элемента массива зависит от реализации.
257;;; Лучше явно указывайте:
258
259(make-array '(2) :initial-element 'unset) ; => #(UNSET UNSET)
260
261;;; Для доступа к элементу в позиции 1, 1, 1:
262
263(aref (make-array (list 2 2 2)) 1 1 1) ; => 0
264
265
266;;; Вектора с изменяемой длиной
267
268;;; Вектора с изменяемой длиной при выводе на консоль выглядят также,
269;;; как и вектора, с константной длиной
270
271(defparameter *adjvec* (make-array '(3) :initial-contents '(1 2 3)
272 :adjustable t :fill-pointer t))
273*adjvec* ; => #(1 2 3)
274
275;;; Добавление новых элементов
276
277(vector-push-extend 4 *adjvec*) ; => 3
278*adjvec* ; => #(1 2 3 4)
279
280
281;;; Множества, это просто списки:
282
283(set-difference '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1)
284(intersection '(1 2 3 4) '(4 5 6 7)) ; => 4
285(union '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1 4 5 6 7)
286(adjoin 4 '(1 2 3 4)) ; => (1 2 3 4)
287
288;;; Несмотря на все, для действительно больших объемов данных, вам нужно что-то
289;;; лучше, чем просто связанные списки
290
291;;; Словари представлены хеш таблицами.
292
293;;; Создание хеш таблицы:
294
295(defparameter *m* (make-hash-table))
296
297;;; Установка пары ключ-значение
298
299(setf (gethash 'a *m*) 1)
300
301;;; Возврат значения по ключу
302
303(gethash 'a *m*) ; => 1, T
304
305;;; CL выражения умеют возвращать сразу несколько значений.
306
307(values 1 2) ; => 1, 2
308
309;;; которые могут быть распределены по переменным с помощью MULTIPLE-VALUE-BIND
310
311(multiple-value-bind (x y)
312 (values 1 2)
313 (list y x))
314
315; => '(2 1)
316
317;;; GETHASH как раз та функция, которая возвращает несколько значений. Первое
318;;; значение - это значение по ключу в хеш таблицу. Если ключ не был найден,
319;;; возвращает NIL.
320
321;;; Второе возвращаемое значение, указывает был ли ключ в хеш таблице. Если ключа
322;;; не было, то возвращает NIL. Таким образом можно проверить, это значение
323;;; NIL, или ключа просто не было.
324
325;;; Вот возврат значений, в случае когда ключа в хеш таблице не было:
326
327(gethash 'd *m*) ;=> NIL, NIL
328
329;;; Можете задать значение по умолчанию.
330
331(gethash 'd *m* :not-found) ; => :NOT-FOUND
332
333;;; Давайте обработаем возврат несколько значений.
334
335(multiple-value-bind (a b)
336 (gethash 'd *m*)
337 (list a b))
338; => (NIL NIL)
339
340(multiple-value-bind (a b)
341 (gethash 'a *m*)
342 (list a b))
343; => (1 T)
344
345
346;;;-----------------------------------------------------------------------------
347;;; 3. Функции
348;;;-----------------------------------------------------------------------------
349
350;;; Для создания анонимных функций используйте LAMBDA. Функций всегда возвращают
351;;; значение последнего своего выражения. Как выглядит функция при выводе в консоль
352;;; зависит от реализации.
353
354(lambda () "Привет Мир") ; => #<FUNCTION (LAMBDA ()) {1004E7818B}>
355
356;;; Для вызова анонимной функции пользуйтесь FUNCALL
357
358(funcall (lambda () "Привет Мир")) ; => "Привет мир"
359(funcall #'+ 1 2 3) ; => 6
360
361;;; FUNCALL сработает и тогда, когда анонимная функция стоит в начале
362;;; неэкранированного списка
363
364((lambda () "Привет Мир")) ; => "Привет Мир"
365((lambda (val) val) "Привет Мир") ; => "Привет Мир"
366
367;;; FUNCALL используется, когда аргументы заранее известны.
368;;; В противном случае используйте APPLY
369
370(apply #'+ '(1 2 3)) ; => 6
371(apply (lambda () "Привет Мир") nil) ; => "Привет Мир"
372
373;;; Для обычной функции с именем используйте DEFUN
374
375(defun hello-world () "Привет Мир")
376(hello-world) ; => "Привет Мир"
377
378;;; Выше видно пустой список (), это место для определения аргументов
379
380(defun hello (name) (format nil "Hello, ~A" name))
381(hello "Григорий") ; => "Привет, Григорий"
382
383;;; Можно указать необязательные аргументы. По умолчанию они будут NIL
384
385(defun hello (name &optional from)
386 (if from
387 (format t "Приветствие для ~A от ~A" name from)
388 (format t "Привет, ~A" name)))
389
390(hello "Георгия" "Василия") ; => Приветствие для Георгия от Василия
391
392;;; Можно явно задать значения по умолчанию
393
394(defun hello (name &optional (from "Мира"))
395 (format nil "Приветствие для ~A от ~A" name from))
396
397(hello "Жоры") ; => Приветствие для Жоры от Мира
398(hello "Жоры" "альпаки") ; => Приветствие для Жоры от альпаки
399
400;;; Можно также задать именованные параметры
401
402(defun generalized-greeter (name &key (from "Мира") (honorific "Господин"))
403 (format t "Здравствуйте, ~A ~A, от ~A" honorific name from))
404
405(generalized-greeter "Григорий")
406; => Здравствуйте, Господин Григорий, от Мира
407
408(generalized-greeter "Григорий" :from "альпаки" :honorific "гражданин")
409; => Здравствуйте, Гражданин Григорий, от альпаки
410
411
412;;;-----------------------------------------------------------------------------
413;;; 4. Равенство или эквивалентность
414;;;-----------------------------------------------------------------------------
415
416;;; У CL сложная система эквивалентности. Взглянем одним глазом.
417
418;;; Для чисел используйте `='
419(= 3 3.0) ; => T
420(= 2 1) ; => NIL
421
422;;; Для идентичености объектов используйте EQL
423(eql 3 3) ; => T
424(eql 3 3.0) ; => NIL
425(eql (list 3) (list 3)) ; => NIL
426
427;;; Для списков, строк, и битовых векторов - EQUAL
428(equal (list 'a 'b) (list 'a 'b)) ; => T
429(equal (list 'a 'b) (list 'b 'a)) ; => NIL
430
431
432;;;-----------------------------------------------------------------------------
433;;; 5. Циклы и ветвления
434;;;-----------------------------------------------------------------------------
435
436;;; Ветвления
437
438(if t ; проверямое значение
439 "случилась истина" ; если, оно было истинно
440 "случилась ложь") ; иначе, когда оно было ложно
441; => "случилась истина"
442
443;;; В форме ветвления if, все не-NIL значения это `истина`
444
445(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(GROUCHO ZEPPO)
446(if (member 'Groucho '(Harpo Groucho Zeppo))
447 'yep
448 'nope)
449; => 'YEP
450
451;;; COND это цепочка проверок для нахождения искомого
452(cond ((> 2 2) (error "мимо!"))
453 ((< 2 2) (error "опять мимо!"))
454 (t 'ok)) ; => 'OK
455
456;;; TYPECASE выбирает ветку исходя из типа выражения
457(typecase 1
458 (string :string)
459 (integer :int))
460; => :int
461
462
463;;; Циклы
464
465;;; С рекурсией
466
467(defun fact (n)
468 (if (< n 2)
469 1
470 (* n (fact(- n 1)))))
471
472(fact 5) ; => 120
473
474;;; И без
475
476(defun fact (n)
477 (loop :for result = 1 :then (* result i)
478 :for i :from 2 :to n
479 :finally (return result)))
480
481(fact 5) ; => 120
482
483(loop :for x :across "abc" :collect x)
484; => (#\a #\b #\c #\d)
485
486(dolist (i '(1 2 3 4))
487 (format t "~A" i))
488; => 1234
489
490
491;;;-----------------------------------------------------------------------------
492;;; 6. Установка значений в переменные (и не только)
493;;;-----------------------------------------------------------------------------
494
495;;; Для присвоения переменной нового значения используйте SETF. Это уже было
496;;; при работе с хеш таблицами.
497
498(let ((variable 10))
499 (setf variable 2))
500; => 2
501
502;;; Для функционального подхода в программировании, старайтесь избегать измений
503;;; в переменных.
504
505;;;-----------------------------------------------------------------------------
506;;; 7. Классы и объекты
507;;;-----------------------------------------------------------------------------
508
509;;; Никаких больше животных в примерах. Берем устройства приводимые в движение
510;;; мускульной силой человека.
511
512(defclass human-powered-conveyance ()
513 ((velocity
514 :accessor velocity
515 :initarg :velocity)
516 (average-efficiency
517 :accessor average-efficiency
518 :initarg :average-efficiency))
519 (:documentation "Устройство движимое человеческой силой"))
520
521;;; Аргументы DEFCLASS:
522;;; 1. Имя класса
523;;; 2. Список родительских классов
524;;; 3. Список полей
525;;; 4. Необязательная метаинформация
526
527;;; Если родительские классы не заданы, используется "стандартный" класс
528;;; Это можно *изменить*, но хорошенько подумайте прежде. Если все-таки
529;;; решились вам поможет "Art of the Metaobject Protocol"
530
531(defclass bicycle (human-powered-conveyance)
532 ((wheel-size
533 :accessor wheel-size
534 :initarg :wheel-size
535 :documentation "Diameter of the wheel.")
536 (height
537 :accessor height
538 :initarg :height)))
539
540(defclass recumbent (bicycle)
541 ((chain-type
542 :accessor chain-type
543 :initarg :chain-type)))
544
545(defclass unicycle (human-powered-conveyance) nil)
546
547(defclass canoe (human-powered-conveyance)
548 ((number-of-rowers
549 :accessor number-of-rowers
550 :initarg :number-of-rowers)))
551
552;;; Если вызвать DESCRIBE для HUMAN-POWERED-CONVEYANCE то получите следующее:
553
554(describe 'human-powered-conveyance)
555
556; COMMON-LISP-USER::HUMAN-POWERED-CONVEYANCE
557; [symbol]
558;
559; HUMAN-POWERED-CONVEYANCE names the standard-class #<STANDARD-CLASS
560; HUMAN-POWERED-CONVEYANCE>:
561; Documentation:
562; A human powered conveyance
563; Direct superclasses: STANDARD-OBJECT
564; Direct subclasses: UNICYCLE, BICYCLE, CANOE
565; Not yet finalized.
566; Direct slots:
567; VELOCITY
568; Readers: VELOCITY
569; Writers: (SETF VELOCITY)
570; AVERAGE-EFFICIENCY
571; Readers: AVERAGE-EFFICIENCY
572; Writers: (SETF AVERAGE-EFFICIENCY)
573
574;;; CL задизайнен как интерактивная система. В рантайме доступна информация о
575;;; типе объектов.
576
577;;; Давайте посчитаем расстояние, которое пройдет велосипед за один оборот колеса
578;;; по формуле C = d * pi
579
580(defmethod circumference ((object bicycle))
581 (* pi (wheel-size object)))
582
583;;; PI - это константа в CL
584
585;;; Предположим мы нашли, что критерий эффективности логарифмически связан
586;;; с гребцами каноэ. Тогда вычисление можем сделать сразу при инициализации.
587
588;;; Инициализируем объект после его создания:
589
590(defmethod initialize-instance :after ((object canoe) &rest args)
591 (setf (average-efficiency object) (log (1+ (number-of-rowers object)))))
592
593
594;;; Давайте проверим что получилось с этой самой эффективностью...
595
596(average-efficiency (make-instance 'canoe :number-of-rowers 15))
597; => 2.7725887
598
599
600;;;-----------------------------------------------------------------------------
601;;; 8. Макросы
602;;;-----------------------------------------------------------------------------
603
604;;; Макросы позволяют расширить синаксис языка. В CL нет например цикла WHILE,
605;;; но его проще простого реализовать на макросах. Если мы отбросим наши
606;;; ассемблерные (или алгольные) инстинкты, мы взлетим на крыльях:
607
608(defmacro while (condition &body body)
609 "Пока `условие` истинно, выполняется `тело`.
610`Условие` проверяется перед каждым выполнением `тела`"
611 (let ((block-name (gensym)) (done (gensym)))
612 `(tagbody
613 ,block-name
614 (unless ,condition
615 (go ,done))
616 (progn
617 ,@body)
618 (go ,block-name)
619 ,done)))
620
621;;; Взглянем на более высокоуровневую версию этого макроса:
622
623(defmacro while (condition &body body)
624 "Пока `условие` истинно, выполняется `тело`.
625`Условие` проверяется перед каждым выполнением `тела`"
626 `(loop while ,condition
627 do
628 (progn
629 ,@body)))
630
631;;; В современных комиляторах LOOP так же эффективен как и приведенный
632;;; выше код. Поэтому используйте его, его проще читать.
633
634;;; В макросах используются символы ```, `,` и `@`. ``` - это оператор
635;;; квазиквотирования - это значит что форма исполнятся не будет, а вернется
636;;; как данные. Оператаор `,` позволяет исполнить форму внутри
637;;; квазиквотирования. Оператор `@` исполняет форму внутри квазиквотирования
638;;; но полученный список вклеивает по месту.
639
640;;; GENSYM создаёт уникальный символ, который гарантировано больше нигде в
641;;; системе не используется. Так надо потому, что макросы разворачиваются
642;;; во время компиляции и переменные объявленные в макросе могут совпасть
643;;; по имени с переменными в обычном коде.
644
645;;; Дальнйешую информацию о макросах ищите в книгах Practical Common Lisp
646;;; и On Lisp
Для чтения ¶
На русском
На английском
Дополнительная информация ¶
На русском
На английском