2012-01-20 8 views
3

I持ってREPLで何が起こっ以下:基本的にClojureのダイナミックレット?

mathematics.core> (let [zebra 1] (resolve 'zebra)) 
nil 
mathematics.core> (def zebra 1) 
#'mathematics.core/zebra 
mathematics.core> (let [zebra 2] (when (resolve 'zebra) (eval 'zebra))) 
1 

、私は変数に値にアクセスすることができ、動的にそのフォーム内の関数をletフォームのようなものを使用して変数に値をバインドし、持っていると思いますに縛られている。

mathematics.core> (def ^:dynamic zebra 1) 
#'mathematics.core/zebra 
mathematics.core> (binding [zebra 2] (when (resolve 'zebra) (eval 'zebra))) 
2 

binding私が欲しいトリックを行うようだが、私の知る限り、それは最初:dynamicメタデータを定義する変数が必要です。前もって定義されたことのない変数を使用することができ、実際に定義されているかのようにその変数にアクセスできるようにしたい説明するために

、私はこのような何かをしたい:

mathematics.core> (let-dynamic [undefined-variable 1] 
        (when (resolve 'undefined-variable) (eval 'unresolved-variable))) 
1 

はこれを行う簡単な方法はありますか?それともマクロを使ってこれを達成する方法ですか?

+3

[XY問題](http://www.perlmonks.org/?node_id=542341)のような匂いがします。 –

答えて

3

。シンボルが定義されていない場合、Clojureコンパイラはそれを使用するコードをコンパイルできません。あなたは、必要なときに遅延を呼び出すマクロを使って何らかのハックを行うことができるかもしれませんが、かなり厄介なコードになります.....

私はバインディングを使用して、 。この方法でコードを書くことができるはずです。

"オンザフライ"で変数を定義するのは悪い考えです。コードで変数を使用している場合は、使用する変数ごとにあらかじめ(def ^:dynamic ...)を実行するだけで十分です。

3

私はその場で 前に定義されていなかった変数を使用して、フォームの式は、それが実際に定義されたかのように、その 変数にアクセスできるように持っていることができるようにしたいです。

使用しない理由は、とにかくこれはClojureののVARSか聞かせて結合値のために良い試合のように私には見えていない、とあなたはその場で全体フォームを生成し、eval'ingしている場合シンボル - >値マッピングを格納し、解決/評価スキーム全体をマップルックアップに置き換える単純なマップ?そのように、あなたは方法を見つけるためにいくつかのハードでコードを壊すかもしれない任意のあいまいな名前空間の策略なしにその場で任意のシンボルを生成することができます。これは、特によく仕事に行くされていない

(let [my-resolve {'zebra 1}] 
    (println "zebra is " (my-resolve 'zebra))) 
2

それは完全なソリューションではありませんが、ここでの試みです:

(defmacro let-dynamic 
     ([[sym val & more] & body] 
     `(do (when (not (:dynamic (meta (resolve '~sym)))) 
       (def ~(with-meta sym {:dynamic true}) ~sym)) 
      (binding [~sym ~val] 
       [email protected](if (empty? more) 
        body 
        `((let-dynamic ~more [email protected]))))))) 

小さなテスト:何らかの理由で

  1. blub> (def ^:dynamic already-dynamic 'dynamic) 
    #'blub/already-dynamic 
    blub> (def not-dynamic 'not-dynamic) 
    #'blub/not-dynamic 
    blub> (let-dynamic [already-dynamic 2] already-dynamic) 
    2 
    blub> (let-dynamic [not-dynamic 2] not-dynamic) 
    2 
    blub> (let-dynamic [not-dynamic-and-not-defined 2] not-dynamic-and-not-defined) 
    2 
    blub> 
    

    これにはいくつかの問題がありますが複数のシンボル値ペアをLET-DYNAMICに渡すと、期待どおりに動作しません。それらは、LET-DYNAMICへの2回目の呼び出し時にのみバインドされます。これは、上記のコードで行ったように、バインディングを再帰的に構築するのではなく、すべてのシンボル値ペアを最初のバインディング形式に渡すようにマクロを変更することで解決できます。

  2. 以前に存在しなかったVarsはクリーンアップされません。これは、定義されたものとなかったもののマップを保持することで解決できます。
  3. 私はマルチスレッド環境でこれがどのように運賃になるか分かりません。あなたはそれを自分で評価しなければなりません。