2

次のCLコードのフラグメントは、SLIMEを実行しているCCLで期待したとおりに機能しません。最初にを使用してファイルをコンパイルしてロードし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.0LOADREPLでこのファイルを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)))

更新:プラットフォームとバージョンについて言及するのを忘れました。Debiansqueezex86を使用しています。SLIMEのバージョンはDebianunstableからのもの1:20120525-2です。CCLは1.8リリースです。http://svn.clozure.com/publicsvn/openmcl/release/1.8/linuxx86/cclのアップストリームバイナリと、自分で作成したバイナリパッケージの両方で試しました。mentors.debian.netのパッケージcclを参照してください。結果はいずれの場合も同じでした。

この問題は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*)

直後のREPLで.を与えますDOUBLE-FLOATが、を生成します。*read-default-float-format*SINGLE-FLOAT

更新2:Rainerが言ったように、コンパイルは別のスレッドで行われるようです。

スレッドディクショナリall-processesの関数ごと

all-processesCc 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*CommonLispのI/O変数の1つです。WITH-STANDARD-IO-SYNTAXそれらを標準値にバインドし、終了時に以前の値を復元するようなものです。したがって、CCLのSLIMEコードにはそのようなバインディングが有効になっていると思います。これは、次のような他のI/O変数を設定することで確認されます*read-base*-それらもバインドされています。

CCL、Emacs、SLIMEにはいくつかのコード層があり、これをデバッグするのは少し複雑です。

  • EmacsはSLIMEのELISPコードを実行し、SWANKと通信します。
  • SWANKはSLIMEのバックエンドであり、CCLで実行されるCommonLispコードです。
  • SWANKには、ポータブルコードといくつかのCCL固有のコードがあります。

Emacs側では、SLIME/ELISP関数SLIME-COMPILE-AND-LOAD-FILEが使用されます。

SWANK側では、CommonLisp関数swank:compile-file-for-emacsが呼び出されます。後でSWANK:LOAD-FILE呼び出されます。

I / O変数がバインドされている場所がわかりません-おそらくCCLネットワークコードにありますか?

これが答えのようです:

CCLにスレッドローカルI/O変数がある場合、コンパイルは別のスレッドで行われ、REPLスレッドおよび他のスレッドのI/Oバインディングは変更されません。

これは、さまざまなCL実装の違いです。CL標準ではスレッドまたはプロセスが指定されていないため、特別なバインディングがグローバルであるか、デフォルトでスレッドローカルバインディングがあるかどうかも指定されていません。

あなたがそれについて考えるならば、他のスレッドからの変更からスレッドのI/O変数を保護することは理にかなっています...

したがって、これに対処する正しい方法は、各スレッドで独立して、正しいI/O変数値が有効になっていることを確認することです。


なぜそうなっているのかを少し詳しく説明します。

通常、CommonLispの実装は複数のスレッドを実行できます。一部の実装では、異なるコアで同時に実行される同時スレッドも許可されます。これらのスレッドは非常に異なることを行うことができます。1つはREPLを実行し、もう1つはHTTPリクエストに応答し、もう1つはディスクからデータをロードし、もう1つはEメールの内容を読み取ることができます。この場合のLispは、1つのLispシステム内でいくつかの異なるタスクを実行します。

Lispには、I/O操作の動作を決定するいくつかの変数があります。たとえば、読み取り時にどの形式の浮動小数点数が含まれるか、または読み取り時にどの基数が含まれるかなどです。手紙は` read-baseによって行われます。

*read-base*ここで、上記のディスク読み取りスレッドが何らかの目的で16に設定されていると想像してください。ここで、別のスレッドのグローバルを8に変更すると、突然、他のすべてのスレッドのベースが8になります。その結果、ディスク読み取りスレッドは*read-base*、16ではなく8になり、動作が異なります。

したがって、これを何らかの方法で防ぐことは理にかなっています。最も単純なのは、各スレッドで実行中のコードに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

伝承

  • 他のスレッドに表示されている1つのスレッドからグローバルバインディングを変更しようとしないでください。
  • 重要な変数を正しい値にバインドすることにより、他のスレッドでの変更からコードを保護します。
  • 変数値を制御可能な方法でいくつかの値に設定する関数を記述します。
于 2012-08-28T19:03:27.583 に答える