6

後で実行できる Java オブジェクト (この場合は BufferedImage) を Clojure コードに埋め込みたいと考えていますeval

コードの作成はうまくいきます:

(defn f [image]
     `(.getRGB ~image 0 0))
=> #'user/f

(f some-buffered-image)
=> (.getRGB #<BufferedImage BufferedImage@5527f4f9: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 256 height = 256 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0> 0 0)

ただし、それを試みると例外が発生しますeval

(eval (f some-buffered-image))
=> CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: BufferedImage@612dcb8c: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 256 height = 256 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0, compiling:(NO_SOURCE_PATH:1) 

このようなものを作る方法はありますか?

編集:

私がこれをやろうとしている理由は、画像からサンプルを取得するコードを生成できるようにしたいからです。画像はコード生成を行う関数に渡されますfが (上記と同等)、(さまざまな理由で) 後でコンパイルされたコードにパラメーターとして渡すことはできません。

これは、生成されたコードにさらに変換を適用するはるかに大きなコード生成ライブラリの一部であるため、引用されたコードを生成する必要があるため、次のようなことはできません。

(defn f [image] 
  (fn [] (.getRGB image 0 0)))
4

3 に答える 3

3

コンパイル時にオブジェクト(または必要なオブジェクトを作成する方法)を取得するマクロを作成し、そのオブジェクトをバイナリ形式(バイト配列)でシリアル化する必要があると思います。マクロの出力は、参照するシンボルバイト配列と、シリアル化されたデータを逆シリアル化してオブジェクトを取得するために使用できる関数に変換します。

于 2012-05-24T11:45:29.193 に答える
3

何のために必要なのかわかりませんが、次のチートを使用して、任意のオブジェクトに評価されるコードを作成できます。

(def objs (atom []))


(defn make-code-that-evals-to [x] 
    (let [
         nobjs (swap! objs #(conj % x)) 
         i (dec (count nobjs))]
     `(nth ~i @objs)))

次に、次のことができます。

> (eval (make-code-that-evals-to *out*))
#<PrintWriter java.io.PrintWriter@14aed2c>

これは単なる概念実証であり、生成されるオブジェクトがリークします。eval で参照を削除するコードを生成できますが、一度しか eval できません。

編集:リークは、次の(悪!)ハックによって防ぐことができます:

上記のコードは、コードの生成時にオブジェクト参照を外部に格納することにより、eval のコンパイラをバイパスします。これは延期できます。オブジェクト参照は、コンパイラがマクロによってバイパスされている状態で、生成されたコードに格納できます。参照をコードに格納するということは、ガベージ コレクターが正常に動作することを意味します。

重要なのは、オブジェクトをラップするマクロです。元のソリューションが行ったこと (つまり、オブジェクトを外部に格納してコンパイラをバイパスする) を行いますが、コンパイルの直前です。生成された式は外部参照を取得し、リークを防ぐために削除します。

注:これは悪です。マクロの展開時間は、グローバルな副作用が発生する最も望ましくない場所であり、これはまさにこのソリューションが行うことです。

コードは次のとおりです。

(def objs (atom {}))

ここに、一意のキー (辞書) でキー付けされたオブジェクトが一時的に格納されます。

(defmacro objwrap [x sym]
  (do
   (swap! objs #(assoc % sym x) ) ; Global side-effect in macro expansion
   `(let [o# (@objs ~sym)]
      (do
        (swap! objs #(dissoc % ~sym))
        o#))))

xこれは、オブジェクト参照をに、一意のキーをに保持する、生成されたコード内にある邪悪なマクロですsym。コンパイル前に、キーの下の外部ディクショナリにオブジェクトを格納し、symそれを取得して外部参照を削除し、取得したオブジェクトを返すコードを生成します。

(defn make-code-that-evals-to [x]
  (let [s 17]    ; please replace 17 with a generated unique key. I was lazy here.
  `(objwrap ~x ~s)))

ユニークなキーと一緒に、邪悪なマクロでオブジェクトをラップするだけです。

もちろん、結果を評価せずにマクロを展開するだけでは、やはりリークが発生します。

于 2012-05-24T18:50:12.537 に答える
0

そうでない理由: (defmacro m [img] `(.getRGB ~img 0 0)) では、次のように記述できます: (m some-buffered-image)

違いは、f が関数であるため、本体が評価される前に引数が評価されることです。したがって、画像オブジェクト自体が生成されたコード内に配置されます。ただし、マクロの場合、その引数は評価されません。そのため、シンボル some-buffered-image のみがコード内に配置されます。生成されるコードは次のようになります: (.getRGB some-buffered-image 0 0)。ソースコードを直接書くのと同じです。それがあなたが望むものだと思います。

しかし、生成されたコードにオブジェクトを配置できないのはなぜですか? 答えは: はい、できます。例外メッセージが言うことは真実ではありません。生成されたコードにいくつかの種類のオブジェクトを埋め込むことができますが、すべての種類のオブジェクトを埋め込むことはできません。それらには、シンボル、数値、文字、文字列、正規表現パターン、キーワード、ブール値、リスト、マップ、セットなどが含まれます。これらのオブジェクトはすべて、Clojure コンパイラによって理解されます。それらは、他の言語のキーワード、演算子、およびリテラルのようなものです。Clojure コンパイラーがすべての種類のオブジェクトを認識していることを要求できないのと同じように、C または Java コンパイラーがその構文に含まれていないすべての単語を認識していることを要求できないのと同じです。

于 2013-10-09T14:34:29.513 に答える