2

对于运行 SLIME 的 CCL,以下 CL 代码片段无法正常工作。如果我首先使用 编译和加载文件C-c C-k,然后运行

(rdirichlet #(1.0 2.0 3.0) 1.0)

在 SLIME/CCL REPL 中,我收到错误

value 1.0 is not of the expected type DOUBLE-FLOAT.
   [Condition of type TYPE-ERROR]

它适用于 SBCL。我希望(setf *read-default-float-format* 'double-float))允许我使用像1.0. LOAD如果我使用REPL将此文件加载到 CCL 中,它可以工作。我错过了什么?

(eval-when (:compile-toplevel :load-toplevel :execute)
  (require :asdf) (require :cl-rmath) (setf *read-default-float-format* 'double-float))

(defun rdirichlet (alpha rownum)
  ;;(declare (fixnum rownum))
  (let* ((alphalen (length alpha))
    (dirichlet (make-array alphalen :element-type '(double-float 0.0 *) :adjustable nil :fill-pointer nil :displaced-to nil)))
    (dotimes (i alphalen)
      (setf (elt dirichlet i) (cl-rmath::rgamma (elt alpha i) rownum)))
    ;; Divide dirichlet vector by its sum
    (map 'vector #'(lambda (x) (/ x (reduce #'+ dirichlet))) dirichlet)))

更新:我忘了提及我的平台和版本。我正在使用 Debian 挤压 x86。SLIME 的版本来自 Debian 不稳定,1:20120525-2. CCL 是 1.8 版本。我尝试了来自http://svn.clozure.com/publicsvn/openmcl/release/1.8/linuxx86/ccl的上游二进制文件和我创建的二进制包 - 请参阅package ccl at advisors.debian.net。结果在每种情况下都是相同的。

这个问题似乎很可能是特定于 SLIME 的。如果人们可以评论他们是否看到这种行为,那将会很有帮助。C-c C-k另外,如果在基本命令行模式下运行 CCL,那么在 SLIME中的等价物是什么?(LOAD filename), 或者是其他东西?或者,问一个稍微不同的问题,调用什么 CCL 函数C-c C-k

我注意到调用C-c C-k以下代码

(eval-when (:compile-toplevel :load-toplevel :execute)
      (require :asdf) (require :cl-rmath) (setf *read-default-float-format* 'double-float))

(print *read-default-float-format*)

产生DOUBLE-FLOAT,尽管*read-default-float-format*在 REPL 之后立即给出SINGLE-FLOAT

更新 2:正如 Rainer 所说,看起来编译发生在一个单独的线程中。

根据线程字典all-processes中的函数

all-processes使用 Cc Ck 从缓冲区打印给出

(#<PROCESS worker(188) [Active] #x18BF99CE> #<PROCESS repl-thread(12) [Semaphore timed wait] #x187A186E> #<PROCESS auto-flush-thread(11) [Sleep] #x187A1C9E> #<PROCESS swank-indentation-cache-thread(6) [Semaphore timed wait] #x186C128E> #<PROCESS reader-thread(5) [Active] #x186C164E> #<PROCESS control-thread(4) [Semaphore timed wait] #x186BE3BE> #<PROCESS Swank Sentinel(2) [Semaphore timed wait] #x186BD0D6> #<TTY-LISTENER listener(1) [Active] #x183577B6> #<PROCESS Initial(0) [Sleep] #x1805FCCE>)
CL-USER> (all-processes)

在 REPL 中给出

(#<PROCESS repl-thread(12) [Active] #x187A186E> #<PROCESS auto-flush-thread(11) [Sleep] #x187A1C9E> #<PROCESS swank-indentation-cache-thread(6) [Semaphore timed wait] #x186C128E> #<PROCESS reader-thread(5) [Active] #x186C164E> #<PROCESS control-thread(4) [Semaphore timed wait] #x186BE3BE> #<PROCESS Swank Sentinel(2) [Semaphore timed wait] #x186BD0D6> #<TTY-LISTENER listener(1) [Active] #x183577B6> #<PROCESS Initial(0) [Sleep] #x1805FCCE>)

所以似乎#<PROCESS worker(188) [Active] #x18BF99CE>是正在编译的线程。当然,仍然存在为什么这些变量是线程本地的,以及为什么 SBCL 的行为方式不同的问题。

4

1 回答 1

3

我也可以通过 CCL 和一些较旧的(我使用的)SLIME 看到这一点。还没有尝试过使用更新的 SLIME。

SBCL 或 LispWorks 不会发生这种情况。

*read-default-float-format*是 Common Lisp 的 I/O 变量之一。类似的东西WITH-STANDARD-IO-SYNTAX将它们绑定到标准值,并在退出时恢复以前的值。所以我怀疑 CCL 的 SLIME 代码具有这样的绑定效果。这可以通过设置其他 I/O 变量来确认*read-base*- 它们也被绑定。

CCL、Emacs 和 SLIME 有一些代码层,这使得调试它有点复杂。

  • Emacs 运行 SLIME 的 ELISP 代码并与 SWANK 对话。
  • SWANK 是 SLIME 的后端,是在 CCL 中运行的 Common Lisp 代码。
  • SWANK 有可移植的代码和一些 CCL 特定的代码。

在 Emacs 端,SLIME-COMPILE-AND-LOAD-FILE使用了 SLIME/ELISP 函数。

在 SWANK 端swank:compile-file-for-emacs调用 Common Lisp 函数。后来SWANK:LOAD-FILE被叫了。

我看不到 I/O 变量的绑定位置 - 也许它在 CCL 网络代码中?

这似乎是答案

如果 CCL 具有线程本地 I/O 变量,则编译发生在另一个线程中,并且不会更改 REPL 线程中的 I/O 绑定,也不会更改任何其他线程中的 I/O 绑定。

这是各种 CL 实现之间的差异。由于 CL 标准没有指定线程或进程,因此也没有指定特殊绑定是全局的还是默认具有线程局部绑定...

如果您考虑一下,保护线程的 I/O 变量免受其他线程的更改是有意义的……

因此,处理它的正确方法应该是确保在每个线程中独立地确保正确的 I/O 变量值有效。


让我稍微扩展一下为什么事情会像现在这样。

通常一个 Common Lisp 实现可以运行多个线程。一些实现还允许并发线程同时在不同的内核上运行。这些线程可以做非常不同的事情:一个可以运行 REPL,另一个可以响应 HTTP 请求,一个可以从磁盘加载数据,另一个可以读取电子邮件的内容。在这种情况下,Lisp 在一个 Lisp 系统中运行多个不同的任务。

Lisp 有几个变量决定了 I/O 操作的行为。例如,读取时使用哪种格式的浮点数或读取时使用哪种基整数。这封信是由` read-base完成的。

*read-base*现在想象一下,上面的磁盘读取线程出于某种目的将其设置为 16。现在您将另一个线程中的全局更改为 8,然后突然所有其他线程都以 8 为基数。结果:磁盘读取线程将突然看到*read-base*8 而不是 16,并且工作方式不同。

因此,以某种方式防止这种情况是有意义的。最简单的是,在每个线程中,运行代码都有自己的 I/O 值绑定,然后更改*read-base*不会对其他线程产生影响。这些绑定通常由LET函数调用或函数调用引入。通常,代码将负责绑定变量。

另一种防止它的方法是给每个线程一些初始绑定,例如应该包括 I/O 绑定。CCL 就是这样做的。例如,LispWorks 也这样做。但不适用于 I/O 变量。

现在每个 Lisp 都可能为您提供一种不可移植的方式来更改线程局部顶部绑定(CCL 也有这种方式 - 例如(setf ccl:symbol-value-in-process))。但这并不意味着它可能会改变 REPL 中有效的绑定。由于 REPL 本身是一段 Lisp 代码,在线程中运行,它可能已经设置了自己的绑定。

在 CCL 中,您还可以设置全局静态绑定:(CCL::%SET-SYM-GLOBAL-VALUE sym value). 但是如果你使用这样的功能,你可能做错了什么或者你有充分的理由。

CCL 的一些背景:http: //clozure.com/pipermail/openmcl-devel/2011-June/012882.html

知识

  • 不要尝试从对其他线程可见的一个线程更改全局绑定。
  • 通过将关键变量绑定到正确的值来保护您的代码免受其他线程中的更改。
  • 编写一个函数,以可控的方式将变量值设置为某些值。
于 2012-08-28T19:03:27.583 回答