2012-08-28 38 views
2

下面的CL代碼片段不能像我期望的那樣運行SLIME的CCL。如果我 第一編譯並加載使用C-c C-k的文件,然後運行'eval-when'意外的行爲

(rdirichlet #(1.0 2.0 3.0) 1.0) 
在淤泥/ 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這樣的值。如果我在REPL中使用LOAD將此文件加載到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 unstable,1:20120525-2。 CCL是1.8版本。我試着用http://svn.clozure.com/publicsvn/openmcl/release/1.8/linuxx86/ccl的上游二進制文件和我創建的二進制包 - 參見Package ccl at mentors.debian.net。結果在每種情況下都是一樣的。

這個問題很可能是SLIME特有的。如果人們能評論他們是否看到這種行爲,這將是有幫助的。另外, SLIME中的C-c C-k等效於什麼,如果在基本命令行模式下運行CCL? (LOAD filename),還是別的?或者,要問一個稍微不同的問題,呼叫什麼CCL功能是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*) 

調用C-c C-k產生DOUBLE-FLOAT,但即使*read-default-float-format*在REPL隨即給SINGLE-FLOAT

更新2:正如Rainer所說,它看起來像編譯發生在一個單獨的線程中。

每功能all-processesThreads Dictionary

使用抄送CK緩衝打印all-processes

(#<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的行爲不同。

+0

奇怪,適合我 –

+0

@VsevolodDyomkin:你使用什麼版本的SLIME和CCL? –

+0

發生錯誤時'* read-default-float-format *'的實際值是多少? –

回答

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的側上的泥/ elisp的功能SLIME-COMPILE-AND-LOAD-FILE被使用。

在SWANK端,Common Lisp函數swank:compile-file-for-emacs被調用。 後來SWANK:LOAD-FILE被調用。

我看不到I/O變量綁定在哪裏 - 也許它在CCL網絡代碼中?

這似乎是答案

如果CCL具有線程本地I/O變量,然後編譯發生在另一個線程,並在REPL線程不會改變I/O綁定和也沒有在任何其他線程。

這是各種CL實現之間的區別。由於CL標準沒有指定線程或進程,也沒有規定如果特殊的綁定是全球性的或具有線程本地默認綁定......

如果你想想看,保護線程的I/O變量對其他線程的變化是有意義的...

因此,處理它的正確方法應該是確保在每個線程中獨立確保正確的I/O變量值有效。


讓我擴大一下爲什麼事情就像他們一樣。

通常,Common Lisp實現可以運行多個線程。一些實現還允許在不同核心上同時運行併發線程。這些線程可以做非常不同的事情:一個可以運行REPL,另一個可以迴應HTTP請求,一個可以從磁盤加載數據,另一個可以讀取電子郵件的內容。在這種情況下,Lisp在一個Lisp系統內運行幾個不同的任務。

Lisp有幾個變量來決定I/O操作的行爲。例如,讀取哪種格式的浮點數或讀取哪些基本整數。該信由'讀取基地完成。

現在想象一下上面的磁盤讀取線程爲了某種目的而將其*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

絕殺

  • 不要試圖從一個線程用於其他線程可見改變全局綁定。
  • 通過將關鍵變量綁定到正確值來屏蔽其他線程中更改的代碼。
  • 編寫一個函數,以可控方式將變量值設置爲某些值。
+0

嗨萊納。感謝你的回答。小語法評論:你寫了「如果CCL具有線程本地I/O變量,那麼如果」。我想你不想要第二個「如果」。所以,你是說綁定在一個編譯發生的線程中被改變了,但是不影響另一個線程?我不明白爲什麼只有一個線程不能使用。是否有某種方式確定是否涉及多個線程?此外,我會重寫答案,以更清楚地回答問題的答案。你有「這似乎是答案:」,接着是「好吧,另一個想法:」,這有點令人困惑。 –

+0

另外,在這個特定的環境下,如何獲得預期的結果? IE瀏覽器。如果以這種方式使用'eval-when'不起作用。 –