如何在 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: 这是我的工作解决方案。事实证明,有两件事是错误的。我的问题需要:

  1. 使用多个 --eval

正如Svante和ignis volens所说

  1. (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)的工作方式分为三个阶段(实际上不止三个,但三个就足够了):

  1. 读取一系列字符以将其转换为表格;
  2. 各种魔法发生在那个形式上;
  3. 评估第 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”。