作为前言,我在 Windows 7(64 位)上运行 Java 版本 6(更新 33),使用clooj作为我的 IDE。我没有尝试在任何其他系统中重现我的问题。我对 Clojure 有经验,但对 Java 一点经验都没有。
我试图解决的整个问题描述起来很长,但可以归结为这一点:假设我想创建一个宏,它接受一个参数、一个关联映射,并返回一个由元素组成的向量。保留其顺序的地图。
=>(defmacro vectorize-a-map
[associative-map]
(vec associative-map))
=>#'ns/vectorize-a-map
=>(vectorize-a-map {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8}
=>[[:a 1] [:b 2] [:c 3] [:d 4] [:e 5] [:f 6] [:g 7] [:h 8]]
这可行,但是向地图添加另一个元素,顺序就会混乱......
=>(vectorize-a-map {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9}
=>[[:a 1] [:c 3] [:b 2] [:f 6] [:g 7] [:d 4] [:e 5] [:i 9] [:h 8]]
我相信我已经发现为什么会发生这种情况。看起来任何具有 8 个或更少元素的东西都会被实例化为 PersistedArrayMap,这正是我想要的,因为据我所知,此类保留了顺序。然而,任何具有 9 个或更多元素的对象都会被实例化为 PersistentHashMap,它不保留顺序。
=>(type {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8}
=>clojure.lang.PersistentArrayMap
=>(type {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9}
=>clojure.lang.PersistentHashMap
我希望我的宏能够采用任何大小的关联贴图,所以这是一个问题。我尝试过类型提示、解构绑定、for列表理解和取消引用拼接,都没有成功。为了把它画出来,以下任何一种方法都不起作用:
(defmacro vectorize-a-map
[^clojure.lang.PersistentArrayMap associative-map]
(vec associative-map))
(defmacro vectorize-a-map
[[& associative-map]]
(vec associative-map))
(defmacro vectorize-a-map
[associative-map]
(vec
(for [x associative-map]
x)))
(defmacro vectorize-a-map
[associative-map]
`(vector ~@associative-map))
通过我提出的这个玩具问题,我意识到我可以简单地像这样编写我的宏,并完全避免这个问题:
=>(defmacro vectorize-kvs
[& elements]
(vec (map vec (partition 2 elements))))
=>#'ns/vectorize-kvs
=>(vectorize-kvs :a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9)
=>[[:a 1] [:b 2] [:c 3] [:d 4] [:e 5] [:f 6] [:g 7] [:h 8] [:i 9]]
然而,对于actual我正在尝试解决的问题(我还没有解决这个问题),宏能够采用关联映射很重要(尽管不是 100% 必要)。看起来我正在寻找如何在任何事情发生之前将参数转换为 PersistentArrayMap 。可能还有其他一些我根本没有考虑或意识到的解决方案。
我已经进行了我所知道的最好的研究,但还没有发现任何有用的东西。有人有什么想法/建议吗?