Я новичок в Common Lisp и хочу использовать библиотеку.
Я не могу найти ни одного простого примера загрузки/требования/использования модуля. Я установил cl-ppcre следующим образом:
$ sbcl --non-interactive --eval '(ql:quickload "cl-ppcre")'
To load "cl-ppcre":
Load 1 ASDF system:
cl-ppcre
; Loading "cl-ppcre"
..
Но я не знаю, как на самом деле использовать его. Я пробовал следующее и дюжину других вещей, и ни одна из них не работает.
$ sbcl --noinform --non-interactive --eval '(progn (require "cl-ppcre") (cl-ppcre:split "\s+" "1 2 3"))'
Unhandled SB-INT:SIMPLE-READER-PACKAGE-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING
{1004DB8073}>:
Package CL-PPCRE does not exist.
Stream: #<dynamic-extent STRING-INPUT-STREAM (unavailable) from "(progn (...">
Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {1004DB8073}>
0: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<SB-INT:SIMPLE-READER-PACKAGE-ERROR "Package ~A does not exist." {1003640A83}> #<unused argument> :QUIT T)
1: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<SB-INT:SIMPLE-READER-PACKAGE-ERROR "Package ~A does not exist." {1003640A83}>)
2: (INVOKE-DEBUGGER #<SB-INT:SIMPLE-READER-PACKAGE-ERROR "Package ~A does not exist." {1003640A83}>)
3: (ERROR #<SB-INT:SIMPLE-READER-PACKAGE-ERROR "Package ~A does not exist." {1003640A83}>)
Итак, как я могу заставить его работать?
РЕДАКТИРОВАТЬ 1: Я не уточнил, что моя проблема с использованием библиотек связана как со скриптами, так и с терминалом. Это было неявно для меня. Это из-за моего опыта работы с Perl, в котором все, что можно сделать с файлом, можно делать в командной строке, в том числе с помощью библиотек.
Обновлено еще раз: Вот мое рабочее решение. Как оказалось, были 2 вещи не так. Моя проблема требует:
--eval
Как и сказали Сванте и Игнис Воленс.
(load "~/.quicklisp/setup.lisp")
Что мне уже объяснили здесь:
Запутался в ``ql:quickload`` и исполняемых скриптах в SBCL
Это терминальное решение:
sbcl --non-interactive --eval '(load "~/.quicklisp/setup.lisp")' --eval '(require :cl-ppcre)' --eval '(princ (cl-ppcre:split "\\s+" "1 2 3"))'
С той оговоркой, что на stderr выводится куча предупреждений, вроде этого, и я не знаю, почему так.
WARNING: redefining QL-SETUP:QMERGE in DEFUN
А это скриптовое решение:
#!/usr/bin/sbcl --script
(load "~/.quicklisp/setup.lisp")
(require :cl-ppcre)
(princ (cl-ppcre:split "\\s+" "1 2 3"))
(terpri)
Ты почти там.
Вы можете использовать (ql:quickload package)
для установки и использования. Если его нет в вашей системе, он будет загружен.
Если вы также не импортируете пакет, вы должны указать префикс функции с именем библиотеки.
Можете ли вы заставить это работать в вашей системе разработки?
(ql:quickload :arrow-macros)
(defun do-it ()
(arrow-macros:->>
'(1 2 3 4)
(mapcar #'1+)
(reduce #'+)
))
Хорошо, я укушу. Почему вы не хотите использовать REPL ??
Загружаются системы, а не пакеты. «пакет» в Common Lisp — это термин для его конструкций пространства имен.
@FrancisKing В конце концов я буду использовать REPL, и очень скоро. Но поскольку мой предыдущий рабочий процесс с языком сценариев был полностью выполнен с помощью терминала и файлов, я хотел, чтобы это было охвачено. Но это нечто большее. Сначала я не уточнял, но это была проблема не только с использованием библиотек в однострочниках, но и в простых файлах. В конце концов, продуктом программирования являются файлы и сценарии, поэтому, как вы знаете, возможность вызывать библиотеки из файловых сценариев невозможна.
@RainerJoswig Я понятия не имею, в чем разница. Я посмотрю. Каждый язык имеет более или менее разные способы управления пространствами имен, временем компиляции/временем выполнения, ранними/поздними связываниями и т. д. Что не помогает, так это то, что каждый использует разные слова для обозначения таких вещей. Слова почти лишены своего значения, они не содержат значений напрямую, только ссылки на значения в неизвестном месте.
@WhiteMist Common Lisp SYSTEM - это загружаемое программное обеспечение: библиотека или программа. ПАКЕТ Common Lisp — это пространство имен для символов. Common Lisp унаследовал эти названия «система» и «пакет» от более раннего Lisp 70-х годов.
@WhiteMist Идея такой системы разработки, как Portacle, заключается в том, что вы создаете изображение, которое добавляете в интерактивном режиме. Наконец, вы выгружаете образ как исполняемый файл, который вы запускаете из файла сценария. Настоятельно рекомендуется использовать Portacle.
@Rainer Joswig Я думал, что это будет что-то вроде этого, но полезно знать наверняка. Думаю, сегодня мы бы назвали SYSTEM либо библиотекой, либо модулем компиляции.
@FrancisKing Спасибо за рекомендацию, я проверю ее, когда буду немного более продвинутым. Но насколько распространено создание скомпилированных двоичных файлов на Common Lisp? Это классная возможность или это действительно стандартный и практичный способ создания готовых программ? Я спрашиваю об этом, потому что однажды нашел способ создать скомпилированный двоичный файл из «образа» состояния интерпретатора (SBCL), и он создал двоичный файл размером 30 МБ только для Hello World.
Запуск программ из параметров bash в качестве источника — не самый простой способ начать. Проблема заключается в порядке компиляции, загрузки и выполнения. Грубо говоря, части, которые вы вводите в командной строке, сначала компилируются, а затем загружаются, но сначала необходимо загрузить библиотеку, прежде чем она сможет скомпилировать остальные. Это можно обойти, например. грамм. с несколькими параметрами -e, но я не думаю, что это полезное упражнение для новичка.
Если вы просто хотите попробовать что-то из командной строки, запустите SBCL без параметров, что даст вам приглашение REPL. Это командная строка, которая принимает код Лиспа. Похоже на *
в ванильном SBCL.
[user@home]> sbcl
This is SBCL …
*
В этом приглашении загрузите свою библиотеку:
* (ql:quickload "cl-ppcre")
To load "cl-ppcre":
Load 1 ASDF system:
cl-ppcre
; Loading "cl-ppcre"
[package cl-ppcre]................................
..........................
("cl-ppcre")
*
Затем используйте его:
* (cl-ppcre:split "\s+" "1 2 3")
("1 2 3")
*
Затем вы можете выйти из REPL:
* (quit)
[user@home]>
Для серьезной работы используйте файлы.
Это пример общей проблемы, с которой сталкиваются люди с CL.
Работа CL (и других Лиспов) состоит из трех фаз (на самом деле их больше трех, но трех достаточно):
Обычно этот процесс итерируется для обработки, скажем, файла или потока ввода.
Важно то, что (1), (2) и (3) происходят в последовательности: (1) завершается до начала (2), и оба (1) и (2) завершаются до начала (3).
Это означает, что (1) должно быть возможно до того, как произойдет что-либо из (2) или (3).
Итак, рассмотрим эту форму:
(progn
(ql:quickload "cl-ppcre")
(cl-ppcre:split "\s+" "1 2 3"))
(Это в значительной степени одна из форм, которые вы пытаетесь оценить.)
Итак, вопрос: что нужно, чтобы (1) произошло? Ну, это требует двух вещей:
QL
(который, по сути, является пространством имен: см. ниже подробнее о том, что означает «пакет» в CL) должен существовать, чтобы читать ql:quickload
;CL-PPCRE
должен существовать, чтобы читать cl-ppcre:split
.И теперь вы видите проблему: (ql:quickload "cl-ppcre")
создает пакет CL-PPCRE
, и это не происходит до (3). Это означает, что эта форма не может быть прочитана.
Обойти это можно, в отчаянии, разными богатырскими уловками. Однако на самом деле вам это не нужно: вы можете сделать что-то еще, что (почти: см. ниже) работает:
(ql:quickload "cl-ppcre")
(cl-ppcre:split "\s+" "1 2 3")
И это (почти) хорошо, потому что это не одна форма: их две. Итак, (1)-(3) отлично работает для первой формы, а потом (1)-(3) отлично работает для второй формы.
Таким образом, ответ заключается не в том, чтобы пытаться упаковать все в единую форму. Либо поместите вещи в файл (вероятно, лучший способ), либо, если вы действительно хотите запускать вещи в качестве аргументов командной строки, вам нужно сделать так, чтобы формы были отдельными, например, с несколькими параметрами --eval
.
Выше я сказал, что работает вторая, многоформенная, версия почти. И это только почти работает. Причиной этого является компиляция файла. Предположим, у меня есть файл, содержимое которого:
(ql:quickload "cl-ppcre")
(cl-ppcre:scan ...)
...
Итак, что делает компилятор, когда компилирует этот файл? Он читает его, форма за формой, но в целом не фактически выполняет код (есть исключения): он организует выполнение этого кода при загрузке файла. Так что компилятор не будет загружать CL-PPCRE: он устроит жизнь так, что при файле загружен будет загружен CL-PPCRE. И теперь у нас есть версия той же проблемы: вторая форма не может быть прочитана компилятором, потому что пакет CL-PPCRE
еще не существует.
Что ж, для этого есть решение: вы можете сказать компилятору, что он действительно должен выполнять некоторый код во время компиляции:
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload "cl-ppcre"))
(cl-ppcre:scan ...)
...
И теперь благодаря форме eval-when
компилятор знает, что он должен вызывать ql:quickload
. Так оно и будет, поэтому пакет CL-PPCRE
будет определен во время компиляции, и все будет хорошо.
Термин «пакет» в CL имеет значение, которое, к сожалению, не такое, как во многих других языках: это связано с историей и теперь не может быть изменено.
В обычном использовании пакет — это «некоторый фрагмент кода, который вы, возможно, можете установить и который, возможно, имеет зависимости от других пакетов (фрагментов кода), за которыми может следить какой-либо менеджер пакетов. Вы можете установить Python в виде пакета на свой компьютер с Ubuntu или использовать диспетчер пакетов conda
для управления научными пакетами Python (включая сам Python).
В CL пакет — это, по сути, пространство имен. Если я набираю «foo:bar», то это относится к символу с именем BAR
, доступному в пакете, одним из имен или псевдонимов которого является FOO
. Кроме того, это «внешний» символ, что означает, что он каким-то образом предназначен для публичного использования. Пакеты — это реальные объекты в CL, и программы могут их анализировать. Всегда существует понятие текущего пакета, и этот пакет определяет, какие имена доступны, не требуя префиксов пакетов, как за счет непосредственного содержания некоторых имен, так и за счет поиска («использования») списка других пакетов. Пакетов в CL очень много: гораздо больше, чем я могу упомянуть здесь.
То, что обычно называют пакетами, вероятно, лучше всего называть «библиотеками» в CL: это фрагменты кода, которые вы можете установить и которые могут иметь собственные зависимости. В качестве альтернативы их часто называют «системами», поскольку они часто определяются с помощью инструмента «определения системы». «Библиотека», вероятно, является более подходящим термином: «система» — это опять-таки историческая странность, которую уже нельзя изменить.
Спасибо, чувак, что нашел время объяснить это. Этого недостаточно, чтобы заставить меня понять, но достаточно, чтобы я мог начать задавать правильные вопросы и узнавать то, чего я еще не знаю.
@WhiteMist: я добавил примечание о пакетах в CL, которое, как мне кажется, обычно очень сбивает с толку новичков, и какой термин я раньше просто использовал без объяснения причин, что было плохо.
Пакеты
Определения
FOO::BAR обозначает символ с именем BAR в пакете FOO.
FOO:BAR обозначает экспортированный символ с именем BAR в пакете FOO.
BAR обозначает символ BAR в текущем пакете
Чтобы прочитать символ с определенным пакетом, этот пакет должен существовать.
Пример:
В LispWorks есть пакет под названием CAPI -> мы можем его найти.
CL-USER 44 > (find-package "CAPI")
#<The CAPI package, 5109/8192 internal, 880/1024 external>
CL-USER 45 > (read-from-string "CAPI::BAR")
CAPI::BAR
9
выше создает символ с именем BAR в существующем пакете CAPI.
Но нет пакета под названием FOO. Не можем найти:
CL-USER 46 > (find-package "FOO")
NIL
Теперь чтение символа, находящегося в пакете FOO, является ошибкой, так как пакет не существует:
CL-USER 47 > (read-from-string "FOO::BAR")
Error: Reader cannot find package FOO.
1 (continue) Create the FOO package.
2 Use another package instead of FOO.
3 Try finding package FOO again.
4 (abort) Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 48 : 1 >
Проблема
Когда у вас есть одна форма Lisp, вам нужно убедиться, что она не содержит неизвестных пакетов. Поэтому сначала загрузите в систему программное обеспечение, которое также определяет пакеты. Затем найдите символ cl-ppcre:split
по названию split
в упаковке cl-ppcre
:
(progn
(ql:quickload "cl-ppcre")
(funcall (find-symbol "SPLIT" "CL-PPCRE") "\\s+" "1 2 3"))
Выше избегается упоминание символа cl-ppcre:split
. Вместо этого мы просматриваем его во время выполнения через find-symbol
.
Или:
(progn
(ql:quickload "cl-ppcre")
(eval (read-from-string "(cl-ppcre:split \"\\\\s+\" \"1 2 3\")")))
Или: сделайте его двумя формами:
(ql:quickload "cl-ppcre") ; loading the software
; also creates the package CL-PPCRE
(cl-ppcre:split "CL-PPCRE") "\\s+" "1 2 3"))
Да, это работает, но это хак. Мне не нужно возиться с символами только для того, чтобы вызвать функцию библиотеки. Я обнаружил, что замена (ql:quickload "cl-ppcre")
на (require "cl-ppcre")
также работает в этом примере.
@whitemist: это не взлом. Это просто необходимо, так как в противном случае форма читается, ничего не загрузив. В Common Lisp нельзя интернировать символ в несуществующем пакете. Символы представляют собой структуру данных, поэтому их можно создавать или находить во время выполнения, а FUNCALL может вызывать символьную функцию символа. -- единственная реальная альтернатива состоит в том, чтобы иметь две различные формы. Сначала загружается программное обеспечение. Затем вторая форма считывается и выполняется. Таким образом, во время чтения символ и его пакет уже будут существовать.
Я понимаю, на что ты мне намекаешь. Я не считаю это решением проблемы использования библиотеки самым простым способом (вызовом функции), но, по крайней мере, вы учите меня кое-чему о том, как работает Common Lisp.
Я думаю, что понял. В конце нам нужно загрузить библиотеку, которая также создает запись в некоторой таблице символов, а затем нам нужно использовать эту запись пакета в таблице символов, чтобы иметь ссылку на символ /a функции, которую мы хотим вызвать. Это должно происходить последовательно. И поскольку что-то в загрузке библиотеки происходит во время выполнения, мы можем либо иметь 2 формы, вычисляемые последовательно одно за другим, либо мы можем поместить 2 s-выражения в 1 форму. Но поскольку их символы одновременно ограничены, нам приходится вручную задерживать один из них, чтобы сделать привязку последовательной.
Приближенным образом.
@WhiteMist таблица символов является пакет, а символ является записью в этой таблице. также ql:quickload
означает "символ quickload
в упаковке ql
", если вы не знали.
@whitemist: проблема не в привязке, проблема в том, что пакет (пространство имен) существует только после загрузки программного обеспечения (поскольку он содержит необходимое объявление пакета, которое генерирует пакет), поэтому символ my-package:my-name
не может быть прочитан . Чтение означает вызов функции READ для потока и возврат символа. Решения: A) создайте пространство имен (пакет) перед чтением проблемной формы. Б) не включайте в форму оскорбительный символ. --- Это не зависит от привязок, значений и т. д. Просто чтение (-> разбор) s-выражения уже не работает.
На самой странице quicklisp есть пример того, как это сделать: https://www.quicklisp.org/beta/
В примере устанавливается quicklisp и, в конечном итоге, используется библиотека "vecto".
Да, в REPL все работало. Но это не работает в командной строке, я получаю «Пакет ARROW-MACROS не существует».