common-lisp.md

Личный сайт Go-разработчика из Казани

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

Для чтения

На русском

На английском

Дополнительная информация

На русском

На английском