casting - 将保留关联映射顺序的 Clojure 宏

标签 casting macros clojure hashmap associative-array

作为序言,我在 Windows 7(64 位)上,使用 clooj 运行 Java 版本 6(更新 33)作为我的 IDE。我没有尝试在任何其他系统中重现我的问题。我对 Clojure 有经验,但对 Java 完全没有。

我试图解决的整个问题描述起来很长,但归结为:假设我想做一个宏,它接受一个参数,一个关联映射,并返回一个元素的向量保留其顺序的 map 。

=>(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]]

那行得通,但是向 map 添加另一个元素并且顺序困惑了...
=>(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 个或更少元素的东西都被实例化为 PersistentArrayMap,这正是我想要的,因为据我所知,这个类保留了顺序。但是,任何具有 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

我希望我的宏能够获取任何大小的关联 map ,所以这是一个问题。我尝试过类型提示、解构绑定(bind)、列表理解和取消引用拼接,都没有成功。要画出来,以下任何方法都不起作用:
(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]]

然而,对于我试图解决的实际问题(我还没有涉及),宏能够采用关联映射是很重要的(尽管不是 100% 必要)。似乎我正在寻找如何在任何事情发生之前将参数转换为 PersistentArrayMap 。可能还有其他一些我根本没有考虑或不知道的解决方案的途径。

我已经研究了我所知道的最好的方法,但还没有发现任何有用的东西。有人有什么想法/建议吗?

最佳答案

你可以用array-map制作你的 map

user> (map vec (array-map 1 2 3 4 5 6))
([1 2] [3 4] [5 6])

或使用更大的 map
user> (map vec (apply array-map (range 50)))
([0 1] [2 3] [4 5] [6 7] [8 9] [10 11] [12 13] [14 15] [16 17] [18 19] [20 21] [22 23] [24 25] [26 27] [28 29] [30 31] [32 33] [34 35] [36 37] [38 39] [40 41] [42 43] [44 45] [46 47] [48 49])

作为奖励,您可以避免使用宏,这很有用,因为宏不是一流的并且组合不好*

关于 array map 文档中的第一条评论的注释
 Note that an array map will only maintain sort order when un-'modified'. 
 Subsequent assoc-ing will eventually cause it to 'become' a hash-map.

If you find yourself depending on the order of keys in your maps you may want to consider if a sorted-map will get you what you need. It will scale better than an array-map. In the above example the output is the same:

(map vec (apply sorted-map (range 5000)))
[0 1] [2 3] ... [4998 4999]

*这是我的意见

编辑:
sorted-map 的时间比较与 array-map
user> (time (dorun (map vec (apply sorted-map (range 500000)))))
"Elapsed time: 391.520491 msecs"
nil
user> (time (dorun (map vec (apply array-map (range 500000)))))
"Elapsed time: 674517.821669 msecs"

关于casting - 将保留关联映射顺序的 Clojure 宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11130460/

相关文章:

C++ 将 uint8_t 转换为 unsigned long

ios - 发送嵌套消息时正确转换

c - 为什么允许从 int 转换为 void *?

c 预处理器 - 如果在特定日期后编译失败

macros - 如何在 Racket 中将 define-syntax 与 syntax-case 分开

clojure - 两个函数相互递归调用

vector - 为什么键和值不能在向量上工作?

clojure - 为什么 when-let 和 if-let 默认不支持多个绑定(bind)?

mysql - SQL 查询连接 4 个表并从 2 个不同的表中获取 2 列的总和

c++ - 用于重复代码的 C/C++ 宏