mik*_*era 5 eval metaprogramming clojure
我想在Clojure代码中嵌入一个Java对象(在本例中为BufferedImage),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)
Run Code Online (Sandbox Code Playgroud)
但是在尝试时会出现异常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)
Run Code Online (Sandbox Code Playgroud)
有没有办法让这样的工作?
编辑:
我试图这样做的原因是我希望能够生成从图像中采样的代码.图像被传递给执行代码生成的函数(相当于f上面的代码),但是(由于各种原因)以后不能作为参数传递给编译代码.
我需要生成带引号的代码,因为这是一个更大的代码生成库的一部分,它将进一步转换为生成的代码,因此我不能只做类似的事情:
(defn f [image]
(fn [] (.getRGB image 0 0)))
Run Code Online (Sandbox Code Playgroud)
不确定你需要它做什么,但你可以使用以下作弊创建可评估任意对象的代码:
(def objs (atom []))
(defn make-code-that-evals-to [x]
(let [
nobjs (swap! objs #(conj % x))
i (dec (count nobjs))]
`(nth ~i @objs)))
Run Code Online (Sandbox Code Playgroud)
然后你可以:
> (eval (make-code-that-evals-to *out*))
#<PrintWriter java.io.PrintWriter@14aed2c>
Run Code Online (Sandbox Code Playgroud)
这只是一个概念证明,它会泄漏正在生成的对象 - 您可以生成删除 eval 上的引用的代码,但随后您只能对其进行一次 eval。
编辑:可以通过以下(邪恶的!)黑客来防止泄漏:
上面的代码通过在生成代码时在外部存储对象引用来绕过 eval 的编译器。这可以推迟。对象引用可以存储在生成的代码中,并通过宏绕过编译器。将引用存储在代码中意味着垃圾收集器正常工作。
关键是包装对象的宏。它执行原始解决方案的操作(即在外部存储对象以绕过编译器),但就在编译之前。生成的表达式检索外部引用,然后删除以防止泄漏。
注意:这是邪恶的。宏的扩展时间是最不希望发生全局副作用的地方,而这正是该解决方案的作用。
现在的代码:
(def objs (atom {}))
Run Code Online (Sandbox Code Playgroud)
这是临时存储对象的位置,由唯一键(字典)作为键控。
(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#))))
Run Code Online (Sandbox Code Playgroud)
这是位于生成代码中的邪恶宏,将对象引用保留在 中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)))
Run Code Online (Sandbox Code Playgroud)
没什么特别的,只需将对象与唯一的密钥一起包装在邪恶的宏中即可。
当然,如果您只扩展宏而不评估其结果,您仍然会遇到泄漏。