- PVSM.RU - https://www.pvsm.ru -
Nikodemus' Common Lisp FAQ
Это очень неофициальное ЧаВО по языку Common Lisp, отражающее субъективное мнение Nikodemus Siivola. Этот текст частично основан на других ЧаВО, встречавшихся на просторах интернета и comp.lang.lisp.
Если вы думаете, что я воспользовался вашим текстом, буду рад признаться в этом, поэтому пишите на nikodemus@random-state.net.
Текущая версия этого ЧаВО доступна по следующим адресам:
http://random-state.net/files/nikodemus-cl-faq.txt [1]
http://random-state.net/files/nikodemus-cl-faq.html [2]
ЧаВО также периодически публикуется в comp.lang.lisp.
«Common Lisp» — название языка, стандартизированного ANSI.
«CL» — наиболее предпочтительное сокращение предыдущего названия.
«Clisp» не является правильным сокращением, поскольку это название одной из реализаций Common Lisp.
«Lisp» — это категория языков, к которым относится CL.
«LISP» уже не пишут лет 20, Common Lisp часто сокращают до Lisp, если
ясно из контекста, о чём именно идёт речь.
Про сам язык почитать можно тут:
http://random-state.net/features-of-common-lisp.html [3]
Много людей не раз пытаются пройти по первому пути, но забывают про второй.
Ну правда, нельзя же научиться программировать на языке, не начав его использовать. И потом, без работы над достаточно крупной программой многие вещи вообще невозможно понять правильно.
Любая хорошая книга расскажет вам об особенностях и трюках языка, но есть пара психологических и социальных моментов, на которых многие спотыкаются:
Начните с «Practical Common Lisp» (известной также как PCL) Питера Сайбела (Peter Seibel). Это хорошая отправная точка, если вы уже владеете каким-то языком программирования. Книга доступна в электронном и печатном виде:
http://www.gigamonkeys.com/book/ [4]
Другая хорошая книга — это «Common Lisp: A Gentle Introduction to Symbolic Computation» Девида Турецкого (David Touretzky). Новичокам в программировании или тем, кому PCL показалась слишком сложной, стоит почитать эту книгу. Если вы начнете с неё, потом всё же прочитайте PCL. Хотя если вы уже прочитали PCL и всё поняли, эту можно смело пропустить. Книга доступна в электронном и печатном виде:
http://www.cs.cmu.edu/~dst/LispBook/index.html [5]
Есть еще куча хороших книг, но указанные две — лучшие для новичков. «Land of Lisp» неплоха, но по моему скромному опыту она может сформировать неправильное представление. Если начнете с нее, все равно прочитайте PCL.
Обязательно познакомьтесь с «Hyperspec» или CLHS, электронной версией стандарта языка. Это просто ценнейший справочник:
http://www.lispworks.com/documentation/HyperSpec/index.html [6]
Не надо сразу бросаться читать справочник. Просто знайте где он находится, здесь содержатся официальные ответы на все возможные вопросы про CL. Это идеальное место для выяснения, что делает тот или иной оператор. Кроме того, в него стоит заглядывать, чтобы узнать, реализован ли нужный вам функционал в языке.
Не пренебрегайте также документацией, поставляемой вместе с вашей реализацией. В случае SBCL, руководство находится по адресу:
http://www.sbcl.org/manual [7]
Зависит от того, что вам нужно, хотя если вы только начали, то это не важно. Впрочем, если вы ждете помощи от какой-то группы людей, берите реализацию, которой эта группа пользуется.
По мере обучения вы сами сможете сделать осознанный выбор. Переход между реализациями не представляет большой сложности, так что это не повод для тягостных сомнений.
Я очень неравнодушен к SBCL:
SBCL хорош тем, что он идет с открытым исходным кодом, запускается на множестве платформ (в том числе Windows), включает в себя компилятор, очень серьезно относится к совместимости со стандартом ANSI и вообще несет радость и мир во всем мире… и тут я должен упомянуть, что я один из разработчиков SBCL и моя компания Steel Bank Studio Ltd [9] предоставляет коммерческую поддержку для него.
Если SBCL по каким-то причинам вам не подходит, могу предложить пройтись по следующим спискам:
Открытые:
Коммерческие:
На RubyGems очень похож «QuickLisp»:
http://www.quicklisp.org/ [10]
Он предоставляет массу библиотек и управляет зависимостями между ними. Это очень-очень нужный инструмент.
Наиболее близкими к CPAN являются «Cliki» и «common-lisp.net»:
http://www.cliki.net/ [11]
http://www.common-lisp.net/ [12]
… но лучше ничего и не надо, правда.
Если вы работаете с SBCL, то пользуйтесь Emacs и Slime:
http://www.common-lisp.net/project/slime/ [13]
Даже если вы раньше не пользовались Emacs, совершите усилие над собой и используйте Slime — кривая обучения не очень крутая и все команды доступны через меню.
Можно настроить Slime с помощью Quicklisp, смотри ниже раздел «Как настроить окружение?». Начать изучение можно с M-x slime-cheat-sheet
, впрочем, это лишь малая часть возможностей Slime.
При использовании другой реализации пользуйтесь тем IDE, что рекомендует разработчик (хотя Slime работает практически со всеми реализациями).
Для практической работы с Lisp редактор как минимум должен:
Slime умеет все перечисленное и ещё много чего.
Если вам нравится Vi(m), обратите внимание на Slimv, который связывает Vim с частью Slime, написанной на Common Lisp:
http://www.vim.org/scripts/script.php?script_id=2531 [14]
https://bitbucket.org/kovisoft/slimv/ [15]
http://kovisoft.bitbucket.org/tutorial.html [16]
… но я не могу ручаться за это, поскольку не пользуюсь Vim/Slimv.
Хорошее руководство (на момент написания) по получению SBCL, Slime и настройке Quicklisp расположено здесь:
http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-linux/ [17]
http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-osx/ [18]
Указания по настройке Clisp на Windows. Впрочем, нельзя объять необъятное:
http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-windows/ [19]
И да, и нет. Одного GUI, которым пользовались бы все, нет.
Коммерческие Lisp'ы в большинстве своём поставляются с библиотеками GUI, и, похоже, что сторонникам этих реализаций нравятся поставляемые библиотеки. Однако, код для таких библиотек не переносится между Lisp'ами. Если вы пользуетесь коммерческой реализацией и переносимость кода вам не интересна, то выбирайте инструменты, предлагаемые разработчиком. В зависимости от того, как сделана библиотека, код может переносится на разные операционные системы, возможно это именно то, что вам нужно.
В лагере отрытого кода тоже есть несколько решений.
CommonQt — это привязка Common Lisp к библиотеке smoke для Qt:
http://common-lisp.net/project/commonqt/ [20]
LTK построена поверх Tk:
http://www.peter-herth.de/ltk/ [21]
CL-GTK2 и CLG — привязки к GTK+, но я не могу ничего сказать про текущее состояние этих разработок. Стоит также посмотреть на GTK Server.
http://common-lisp.net/project/cl-gtk2/ [22]
http://sourceforge.net/projects/clg/ [23] http://www.gtk-server.org/ [24]
CLIM (Common Lisp Interface Manager) — это почти стандартизированная спецификация API для GUI, довольно сильно отличающаяся от GUI, перечисленных выше. Не ожидайте, что все будет знакомо и понятно.
http://random-state.net/files/how-is-clim-different.html [25]
Многие клянутся, что это лучшее, что есть для построения GUI, другие утверждают, что это совсем не так. Как бы то ни было, большинство коммерческих Lisp'ов реализуют CLIM, и еще есть переносимая открытая библиотека, называемая McCLIM, довольно удобная, хотя в последнее время не особо развиваемая.
http://common-lisp.net/project/mcclim/ [26]
CLX — это переносимый низкоуровневый Lisp-интерфейс к X11, предоставляющий уровень абстракций, сопоставимый с Xlib.
http://www.cliki.net/CLX [27]
https://github.com/sharplispers/clx [28]
Если не строго ограничиваться вопросами GUI, буду неправ, если не упомяну CL-OPENGL, переносимую привязку к API OpenGL, GLU и GLUT:
http://common-lisp.net/project/cl-opengl/ [29]
Не форум конечно, но есть Planet Lisp — агрегатор блогов по Common Lisp. Много интересной информации, без избытка.
LispForum — просто хороший форум:
http://www.lispforum.com/ [31]
но я не ручаюсь, поскольку бываю там нечасто.
Есть ещё группы comp.lang.lisp на Usenet/Google Groups, но они густо населены троллями. Пишущие там авторы довольно грамотны, при этом спекуляции профанов — рядовое явление. Читать эти группы может быть тяжело, но чтобы пользоваться Lisp'ом, читать их не обязательно.
http://groups.google.com/group/comp.lang.lisp [32]
Специализированные списки рассылки имеют намного лучшее отношение сигнал/шум. Все реализации стараются завести собственные пользовательские и справочные списки рассылки, большинство библиотек также создают собственные списки рассылки. Для SBCL есть, например, это:
https://lists.sourceforge.net/lists/listinfo/sbcl-help [33]
Среди open-source разработчиков и пользователей популярен канал #lisp на freenode.org. Имейте, впрочем, ввиду, что на #lisp довольно жестко придерживаются темы, и эта тема Common Lisp, а не «Lisp вообще». Для этого есть канал #lispcafe с гораздо более мягкими правилами.
Довольно активно сообщество разработчиков игр, но я не особо с ним знаком. Гугл вам в помощь.
Профессионалы Common Lisp общаются в списке «pro». Обсуждение других диалектов Lisp'а является офтопиком, вопросы новичков НЕ принимаются.
http://lists.common-lisp.net/mailman/listinfo/pro [34]
Короткий ответ: запускаете Lisp и печатаете:
(compile-file "/path/to/myfile.lisp")
Затем, скорее всего, вам потребуется загрузить (load ...) компилированный файл.
Развернутый ответ: большинство компилируемых языков неинтерактивны — вы компилирует файл из командной строки или IDE, затем запускаете компилированный файл. В Lisp'е все не так.
При том, что в общем случае вы можете превратить ваш проект в исполняемый файл, типичная рабочая сессия не похожа на цикл правка-компиляция-выполнение, как можно было бы ожидать.
Обычно взаимодействие происходит с запущенным Lisp-процессом, который содержит рабочую сессию, в которую вы интерактивно добавляете код.
Например:
M-x slime
запускаем Slime и Lisp;C-c C-c
, что приведет её перекомпиляции;Упомянутая выше аббревиатура ASDF расшифровывается как «Another System Definition Facility». Этой система позволяет указать способ сбора нескольких файлов в единую систему для их загрузки или компиляции одной командой. Чем-то походит на Make.
Ответ зависит от используемой вами реализации. Смотрите в документации. Если говорить про SBCL:
;; Загружаете приложение в SBCL, затем выполняете команду save-lisp-and-die.
;; Точка входа в приложение - MY-FUNCTION.
(save-lisp-and-die "my.exe" :executable t :toplevel 'my-function)
Короткий ответ: везде, где можно используйте FUNCALL
, в остальных случаях используйте APPLY
.
Развернутый ответ: при вызове FUNCALL
должно быть известно количество аргументов. APPLY
(и MULTIPLE-VALUE-CALL
) не требует информации о количестве аргументов.
(defun map-list-with-1 (function list arg)
(mapcar (lambda (elt)
(funcall function elt arg))
list))
(defun map-list-with-n (function list &rest args)
(mapcar (lambda (elt)
(apply function elt args))
list))
Незачем писать MAP-LIST-WITH-1
с помощью APPLY
, вызов FUNCALL
почти наверняка будет более эффективным.
В противовес MAP-LIST-WITH-N
не может быть написан с использованием FUNCALL
, поскольку количество аргументов вызывающей стороне неизвестно. Следует использовать APPLY
.
Короткий ответ: всегда используйте SETF
.
Развернутый ответ: Давным давно, когда еще не было Common Lisp, не было лексических переменных, были только динамические. И не было тогда ни SETQ
, ни SETF
, только SET
.
То, что сегодня пишется как
(setf (symbol-value '*foo*) 42)
записывалось так
(set (quote *foo*) 42)
что со временем сократилось до SETQ
(SET Quoted)
(setq *foo* 42)
Потом появились лексические переменные и SETQ
стали использовать и для их присваивания, так что SETQ
перестал быть просто оберткой вокруг SET
.
Позже кто-то изобрел SETF
(SET Field) как обобщенный способ присвоения значений в структурах данных, зеркальное отображение L-значений в других языках:
x.car := 42;
записывается как
(setf (car x) 42)
Для симметрии и общности, SETF
также включает в себя функциональность SETQ
. Можно сказать, что SETQ
был низкоуровневым примитивом, а SETF
— высокоуровневой операцией.
Потом появились символьные макросы. Поскольку символьные макросы прозрачны, было сделано так, что SETQ
ведет себя как SETF
в случае, когда присваиваемая «переменная» на деле символьный макрос:
(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))
foo => 42
(setq foo 13)
foo => 13
*hidden* => (13 . 42)
И вот мы попадаем в наши дни: SET
и SETQ
по сути атавизм, оставшийся от старых диалектов и, возможно, будет выкинут из того, что будет следующим Common Lisp'ом.
Всегда пользуйтесь SETF
.
Короткий ответ: пишите
(list 1 2 3)
пока не поймете разницу. Если вы пишете
'(1 2 3)
не модифицируйте это деструктивно (т.е. с помощью SORT
или NREVERSE
).
Развернутый ответ: Во-первых, одинарная кавычка — это макрос, преобразующий
'anything
в
(quote anything)
во время чтения, так что
'(1 2 3) === (quote (1 2 3))
Во-вторых, QUOTE
— это специальный оператор, возвращающий свои аргументы невычисленными. Так
'(1 2 3)
возвращает буквальный (literal) список. Как и в большинстве языков модификация буквальных (literal) данных приводит к неопределенным последствиям. Например, компилятор может соединить константы, содержащие литералы:
(let ((a '(1 2 3))
(b '(1 2 3)))
(eq a b)) ; => T или NIL
Следствием является тот факт, что изменяя A, также может измениться и B. Тогда для чего годится QUOTE
? Если, например, у вас есть большие неизменяемые списки, которые компилятор может соединить, то пометка их как буквальные (literal) дает компилятору право так поступить.
Что бы вы ни использовали для объявления переменных, DEFVAR
или DEFPARAMETER
, всегда делайте *ТАКОЕ-ИМЯ*. И не делайте так для локальных переменных.
(defvar *очень-хорошо* ...)
(defvar это-очень-плохо ...)
Зачем? Если вы еще не знаете, что такое специальные переменные, продолжайте читать ту книжку, которую читаете и возвращайтесь как закончите, а пока используйте ушки.
Ушки защищают от двух простых ошибок, которые очень легко сделать.
(defparameter foo "foo!")
(defun say-it ()
(write-line foo))
(defun say-more (foo)
(say-it)
(format t "now say ~A~%" foo))
Теперь
(say-more "bar!")
напечатает
say bar!
now say bar!
вместо ожидаемого
say foo!
now say bar!
… упс!
Обычно вы получите предупреждение времени компиляции и ошибку времени выполнения в случае
(defun foo (bar)
bat)
но если перед этим написать
(defparameter bat "baseball")
то ошибки не будет и вы потратите уйму времени на отладку, пытаясь понять, что не так.
Если пишете код для себя, все равно, ставите вы ушки или нет, но когда вы публикуете код, отсутствие *ушек* означает трату времени других людей. Не делайте так, пожалуйста!
Отсутствие ушек создает ощущение ошибки: когда я вижу
(defparameter нет-ушек ...)
я понимаю, что нужно читать код особенно осторожно, потому как нет никакой гарантии, что код, который с первого взляда выглядит вполне безобидно, не будет иметь нелокальных побочных эффектов или зависимостей.
Всегда ставьте *ушки*. Говорят, из всех правил бывают исключения, но в данном случае очень трудно отыскать настоящее исключение из этого правила.
Хорошего кода,
— Nikodemus
Автор: DmitrySolomennikov
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/faq/7281
Ссылки в тексте:
[1] http://random-state.net/files/nikodemus-cl-faq.txt: http://random-state.net/files/nikodemus-cl-faq.txt
[2] http://random-state.net/files/nikodemus-cl-faq.html: http://random-state.net/files/nikodemus-cl-faq.html
[3] http://random-state.net/features-of-common-lisp.html: http://random-state.net/features-of-common-lisp.html
[4] http://www.gigamonkeys.com/book/: http://www.gigamonkeys.com/book/
[5] http://www.cs.cmu.edu/~dst/LispBook/index.html: http://www.cs.cmu.edu/~dst/LispBook/index.html
[6] http://www.lispworks.com/documentation/HyperSpec/index.html: http://www.lispworks.com/documentation/HyperSpec/index.html
[7] http://www.sbcl.org/manual: http://www.sbcl.org/manual
[8] http://www.sbcl.org/: http://www.sbcl.org/
[9] Steel Bank Studio Ltd: http://sb-studio.net/
[10] http://www.quicklisp.org/: http://www.quicklisp.org/
[11] http://www.cliki.net/: http://www.cliki.net/
[12] http://www.common-lisp.net/: http://www.common-lisp.net/
[13] http://www.common-lisp.net/project/slime/: http://www.common-lisp.net/project/slime/
[14] http://www.vim.org/scripts/script.php?script_id=2531: http://www.vim.org/scripts/script.php?script_id=2531
[15] https://bitbucket.org/kovisoft/slimv/: https://bitbucket.org/kovisoft/slimv/
[16] http://kovisoft.bitbucket.org/tutorial.html: http://kovisoft.bitbucket.org/tutorial.html
[17] http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-linux/: http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-linux/
[18] http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-osx/: http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-osx/
[19] http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-windows/: http://mohiji.nfshost.com/2011/01/modern-common-lisp-on-windows/
[20] http://common-lisp.net/project/commonqt/: http://common-lisp.net/project/commonqt/
[21] http://www.peter-herth.de/ltk/: http://www.peter-herth.de/ltk/
[22] http://common-lisp.net/project/cl-gtk2/: http://common-lisp.net/project/cl-gtk2/
[23] http://sourceforge.net/projects/clg/: http://sourceforge.net/projects/clg/
[24] http://www.gtk-server.org/: http://www.gtk-server.org/
[25] http://random-state.net/files/how-is-clim-different.html: http://random-state.net/files/how-is-clim-different.html
[26] http://common-lisp.net/project/mcclim/: http://common-lisp.net/project/mcclim/
[27] http://www.cliki.net/CLX: http://www.cliki.net/CLX
[28] https://github.com/sharplispers/clx: https://github.com/sharplispers/clx
[29] http://common-lisp.net/project/cl-opengl/: http://common-lisp.net/project/cl-opengl/
[30] http://planet.lisp.org/: http://planet.lisp.org/
[31] http://www.lispforum.com/: http://www.lispforum.com/
[32] http://groups.google.com/group/comp.lang.lisp: http://groups.google.com/group/comp.lang.lisp
[33] https://lists.sourceforge.net/lists/listinfo/sbcl-help: https://lists.sourceforge.net/lists/listinfo/sbcl-help
[34] http://lists.common-lisp.net/mailman/listinfo/pro: http://lists.common-lisp.net/mailman/listinfo/pro
Нажмите здесь для печати.