在Clojure代码中嵌入任意对象
我想在Clojure代码中嵌入一个Java对象(在本例中是一个BuffereImage),以后可以在Clojure代码中嵌入任意对象,clojure,metaprogramming,eval,Clojure,Metaprogramming,Eval,我想在Clojure代码中嵌入一个Java对象(在本例中是一个BuffereImage),以后可以evald 创建代码可以很好地工作: (defn f [image] `(.getRGB ~image 0 0)) => #'user/f (f some-buffered-image) => (.getRGB #<BufferedImage BufferedImage@5527f4f9: type = 2 DirectColorModel: rmask=ff0000
eval
d
创建代码可以很好地工作:
(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)
有没有办法让这样的事情成功
编辑:
我尝试这样做的原因是,我希望能够生成从图像中获取样本的代码。图像被传递给执行代码生成的函数(相当于上面的f
),但是(由于各种原因)以后不能作为参数传递给编译后的代码
我需要生成引用的代码,因为这是一个更大的代码生成库的一部分,它将对生成的代码应用进一步的转换,因此我不能只执行以下操作:
(defn f [image]
(fn [] (.getRGB image 0 0)))
我猜您需要编写一个宏,在编译时获取对象(或创建所需对象的方法),以二进制格式(字节数组)序列化该对象宏的输出应该是-一个引用字节数组的符号和一个可以通过反序列化从序列化数据中获取对象的函数。不确定您需要它做什么,但您可以使用以下方法创建代码,以评估任意对象:
(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>
这里是将临时存储对象的位置,由唯一键(字典)设置关键帧
这是位于生成代码中的邪恶宏,将对象引用保留在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)))
没什么特别的,只要用一个唯一的键将对象包装在邪恶的宏中就可以了
当然,如果只展开宏而不评估其结果,仍然会出现泄漏。为什么不:
(定义宏m[img]`(.getRGB~img 0))
然后你可以写:(m一些缓冲图像)
不同之处在于f是一个函数,因此它的参数将在其主体被求值之前被求值。因此,图像对象本身将被放置在生成的代码中。但是对于宏,它们的参数将不会被计算。因此,代码中只会放置一些缓冲图像的符号。生成的代码将是:(.getRGB某些缓冲图像0)。就像你直接写源代码一样。我想这就是你想要的
但是为什么我不能在生成的代码中放置一个对象呢?答案是:是的,你可以。异常消息所说的不是事实。u可以在生成的代码中嵌入某些类型的对象,但不能嵌入所有类型的对象。它们包括符号、数字、字符、字符串、正则表达式模式、关键字、布尔值、列表、映射、集合等。Clojure编译器将理解所有这些对象。它们就像其他语言中的关键字、运算符和文字。你不能要求Clojure编译器知道所有类型的对象,就像你不能要求C或Java编译器知道语法中未包含的所有单词一样。为什么需要序列化?编译后的代码仍然依赖于外部字节数组,因此为什么不将其设置为对象数组,并将原始对象的引用保留在那里呢?基本上,当宏将在编译时进行计算时,例如:
(嵌入资源“my file.jpeg”)
,宏嵌入资源将读取文件并将其存储为代码中的java数组,这将允许您将图像本身嵌入到代码中谢谢,我想我现在明白了。如果我理解正确,您也可以在生成代码时立即序列化对象(在OP的示例中,在函数f
中),对吗?是否只是eval给出了此错误?宏(例如如果)可以很好地使用它,我想?如果是这样的话,那么可能是因为eval强制内容通过文本(这对eval来说是有意义的)。如果这对您来说是个问题,您可能会在不必要的时候使用eval,而宏可能正是您所需要的。@andrewcooke据我所知,eval
并不会真正强制内容通过文本。Eval(通过编译)生成创建对象的JVM代码。通过文本(通过序列化)是在未知对象上使用的最后手段。@mikera退一步,你能评论一下为什么选择使用引用和求值,而不是执行类似于(defn f[a](fn[](.getRGB a 0))的操作吗
?@mikera如果您可以将Java对象隐藏在某个地方,并且只在生成的代码中嵌入它们的ID,那么这会简单得多。你为什么不能那样做?好主意!但我同意内存泄漏让这个有点nasty@mikera我知道如何修复它,将在时间允许时进行编辑:)@mikera Solution added。我仍然会想办法解决这一问题,因为这似乎有悖常理。
(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#))))
(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)))