如何在 Common Lisp 中使用库?
How to use libraries in Common Lisp?
我是 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 方面的经验,在 Perl 中,您可以对文件执行的所有操作都可以在命令行执行,包括使用库。
编辑 2:
这是我的工作解决方案。事实证明,有两件事是错误的。我的问题需要:
- 使用多个
--eval
正如Svante和ignis volens所说
(load "~/.quicklisp/setup.lisp")
我已经在这里解释过了:
Confused about ``ql:quickload`` and executable scripts in 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 #'+)
))
来自 bash 参数的 运行 程序作为源并不是最简单的启动方式。问题是编译、加载和执行的顺序。粗略地说,你在命令行输入的部分先编译,然后加载,但它需要先加载库才能编译其余部分。 Thre可能是解决这个问题的方法,e。 G。有多个 -e 参数,但我认为这对初学者来说不是一个有用的练习。
如果您只想从命令行尝试一些事情,请在不带参数的情况下启动 SBCL,这将为您提供 REPL 提示。这是一个接受 Lisp 代码的命令行。它看起来像香草 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(和其他 Lisp)的工作方式分为三个阶段(实际上不止三个,但三个就足够了):
- 读取一系列字符以将其转换为表格;
- 各种魔法发生在那个形式上;
- 评估第 2 阶段的结果,可能还会打印结果。
通常,此过程会迭代以处理文件或输入流。
重要的是 (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
包(本质上是一个命名空间:请参阅下文了解 'package' 在 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) 对于第一种形式工作正常,then (1)-(3) 对于第二种形式工作正常。
所以答案不是尝试将所有内容捆绑到一个表单中。要么把东西放在一个文件中(可能是最好的方法),要么如果你真的想 运行 东西作为命令行参数,你需要安排表格是分开的,例如,多个 --eval
选项。
上面我说了第二个 multiple-form,版本 几乎 有效。而且它几乎只起作用。这样做的原因是文件编译。假设我有一个文件,其内容是:
(ql:quickload "cl-ppcre")
(cl-ppcre:scan ...)
...
那么,编译器在编译该文件时会做什么?它逐表读取它,但通常它 不会 实际执行代码(也有例外):它安排在加载文件时执行该代码。所以编译器不会加载CL-PPCRE:它会安排生命,以便在文件加载时加载CL-PPCRE。现在我们有同样问题的一个版本:编译器无法读取第二种形式,因为 CL-PPCRE
包还不存在。
好吧,有一个解决方案:您可以告诉编译器它实际上必须在 compile-time:
处执行一些代码
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload "cl-ppcre"))
(cl-ppcre:scan ...)
...
现在,由于 eval-when
形式,编译器知道它必须调用 ql:quickload
。它会这样做,因此 CL-PPCRE
包将在编译时定义,一切都会很好。
关于 CL 包的注释
CL 中的术语 'package' 的含义与许多其他语言中的含义不同:这是历史原因,现在无法更改。
在通常的用法中,包是“一些代码块,您可以安装这些代码块并且可能依赖于其他包(代码块),所有这些都可能由某种包管理器管理.您可以将 Python 作为一个包安装在您的 Ubuntu 盒子上,或者您可以使用 conda
包管理器来管理科学 Python 包(包括 Python 本身) .
在 CL 中,包本质上是一个 命名空间 。如果我键入 'foo:bar',那么这是指包中可用的名为 BAR
的符号,其中一个名称或昵称是 FOO
。此外,这是一个 'external' 符号,这意味着它在某种程度上是 public。包是 CL 中的真实对象,可以由程序推理。总是有一个当前包的概念,并且该包通过直接包含一些名称并具有其他包的搜索('use')列表来定义可用的名称而不需要包前缀。 CL 中的包有很多:远远超过我在这里可以提到的。
通常称为包的东西在 CL 中可能最好称为 'libraries':它们是您可以安装的代码块,并且它们本身可能具有依赖性。或者,它们通常被称为 'systems',因为它们通常是使用 'system definition' 工具定义的。 'Library' 可能是更好的说法:'system' 又是一个历史怪事,现在无法真正改变。
包
定义
- 库或应用程序称为 系统。
- 包是符号的命名空间
- 符号是通常驻留在包中的命名对象
FOO::BAR 表示包 FOO
中名为 BAR 的符号
FOO:BAR表示包FOO中名为BAR的导出符号
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
上面在现有包 CAPI 中创建了一个名为 BAR 的符号。
但是没有名为 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
找到符号cl-ppcre:split
(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"))
在 quicklisp 页面本身有一个如何执行此操作的示例:
https://www.quicklisp.org/beta/
该示例安装了 quicklisp 并最终使用了库“vecto”。
我是 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 方面的经验,在 Perl 中,您可以对文件执行的所有操作都可以在命令行执行,包括使用库。
编辑 2: 这是我的工作解决方案。事实证明,有两件事是错误的。我的问题需要:
- 使用多个
--eval
正如Svante和ignis volens所说
(load "~/.quicklisp/setup.lisp")
我已经在这里解释过了:
Confused about ``ql:quickload`` and executable scripts in 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 #'+)
))
运行 程序作为源并不是最简单的启动方式。问题是编译、加载和执行的顺序。粗略地说,你在命令行输入的部分先编译,然后加载,但它需要先加载库才能编译其余部分。 Thre可能是解决这个问题的方法,e。 G。有多个 -e 参数,但我认为这对初学者来说不是一个有用的练习。
如果您只想从命令行尝试一些事情,请在不带参数的情况下启动 SBCL,这将为您提供 REPL 提示。这是一个接受 Lisp 代码的命令行。它看起来像香草 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(和其他 Lisp)的工作方式分为三个阶段(实际上不止三个,但三个就足够了):
- 读取一系列字符以将其转换为表格;
- 各种魔法发生在那个形式上;
- 评估第 2 阶段的结果,可能还会打印结果。
通常,此过程会迭代以处理文件或输入流。
重要的是 (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
包(本质上是一个命名空间:请参阅下文了解 'package' 在 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) 对于第一种形式工作正常,then (1)-(3) 对于第二种形式工作正常。
所以答案不是尝试将所有内容捆绑到一个表单中。要么把东西放在一个文件中(可能是最好的方法),要么如果你真的想 运行 东西作为命令行参数,你需要安排表格是分开的,例如,多个 --eval
选项。
上面我说了第二个 multiple-form,版本 几乎 有效。而且它几乎只起作用。这样做的原因是文件编译。假设我有一个文件,其内容是:
(ql:quickload "cl-ppcre")
(cl-ppcre:scan ...)
...
那么,编译器在编译该文件时会做什么?它逐表读取它,但通常它 不会 实际执行代码(也有例外):它安排在加载文件时执行该代码。所以编译器不会加载CL-PPCRE:它会安排生命,以便在文件加载时加载CL-PPCRE。现在我们有同样问题的一个版本:编译器无法读取第二种形式,因为 CL-PPCRE
包还不存在。
好吧,有一个解决方案:您可以告诉编译器它实际上必须在 compile-time:
处执行一些代码(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload "cl-ppcre"))
(cl-ppcre:scan ...)
...
现在,由于 eval-when
形式,编译器知道它必须调用 ql:quickload
。它会这样做,因此 CL-PPCRE
包将在编译时定义,一切都会很好。
关于 CL 包的注释
CL 中的术语 'package' 的含义与许多其他语言中的含义不同:这是历史原因,现在无法更改。
在通常的用法中,包是“一些代码块,您可以安装这些代码块并且可能依赖于其他包(代码块),所有这些都可能由某种包管理器管理.您可以将 Python 作为一个包安装在您的 Ubuntu 盒子上,或者您可以使用 conda
包管理器来管理科学 Python 包(包括 Python 本身) .
在 CL 中,包本质上是一个 命名空间 。如果我键入 'foo:bar',那么这是指包中可用的名为 BAR
的符号,其中一个名称或昵称是 FOO
。此外,这是一个 'external' 符号,这意味着它在某种程度上是 public。包是 CL 中的真实对象,可以由程序推理。总是有一个当前包的概念,并且该包通过直接包含一些名称并具有其他包的搜索('use')列表来定义可用的名称而不需要包前缀。 CL 中的包有很多:远远超过我在这里可以提到的。
通常称为包的东西在 CL 中可能最好称为 'libraries':它们是您可以安装的代码块,并且它们本身可能具有依赖性。或者,它们通常被称为 'systems',因为它们通常是使用 'system definition' 工具定义的。 'Library' 可能是更好的说法:'system' 又是一个历史怪事,现在无法真正改变。
包
定义
- 库或应用程序称为 系统。
- 包是符号的命名空间
- 符号是通常驻留在包中的命名对象
FOO::BAR 表示包 FOO
中名为 BAR 的符号FOO:BAR表示包FOO中名为BAR的导出符号
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
上面在现有包 CAPI 中创建了一个名为 BAR 的符号。
但是没有名为 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
找到符号cl-ppcre:split
(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"))
在 quicklisp 页面本身有一个如何执行此操作的示例: https://www.quicklisp.org/beta/
该示例安装了 quicklisp 并最终使用了库“vecto”。