Clojure宏将保存关联映射顺序 [英] Clojure macro that will conserve associative map order

查看:105
本文介绍了Clojure宏将保存关联映射顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前言是,我使用 clooj 作为运行Java版本6(更新33)的Windows 7(64位)我的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] :h 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
pre>

我想我的宏能够获取任何大小的关联映射,所以这是一个问题我试过了类型提示,解构绑定,

 <$ c $ 

c>(defmacro vectorize-a-map
[^ clojure.lang.PersistentArrayMap associative-map]
(vec associative-map))

(defmacro vectorize-a-map
[[& associative-map]]
(vec关联地图))

(defmacro vectorize-a-map
[associative-map]
(vec
(for [x associative-map]
x)))

(defmacro vectorize-a-map
[associative-map]
`(向量〜@关联映射))

可以简单地写我的宏,这样,并避免的问题:

  =>(defmacro vectorize-kvs 
[&元素]
(vec(map vec(partition 2 elements))))
=>#'ns / vectorize-kvs
=>(vectorize-kvs:a 1:b 2 :c3:d4:e5:f6:g7:h8:i9)
=> [[:a 1] [:b 2] [:c 3] [:d 4] [:e 5] [:f 6] [:g 7] [:h 8] [:i 9]]

但是,对于我试图解决(我没有得到)的实际问题,重要的是(虽然不是100%必要)宏能够采取关联地图。看起来我正在寻找如何将参数转换为PersistentArrayMap之前任何东西都有机会发生它。可能有一些其他途径,我只是没有考虑或不知道的解决方案。



我研究了最好的我知道如何,没有找到任何有用的东西。是否有任何想法/建议?

解决方案

您可以使用数组映射

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

或更大的地图

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

*





a注意:这是一个非常有用的宏,因为宏不是一流的,您的阵列图文档中的第一条评论

请注意,数组映射只有在未修改时才保持排序顺序。 
后续的assoc- ing会最终导致它成为一个哈希映射。

如果你发现自己取决于键的顺序如果 sorted-map 会得到你需要的,你可能需要考虑你的地图。它将比 array-map 更好地缩放。在上面的例子中输出是相同的:

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

意见





编辑:



一次时间比较 sorted-map array-map

  user> (时间(dorun(地图vec(应用排序地图(范围500000)))))
经过时间:391.520491毫秒
nil
用户> (time(dorun(map vec(apply array-map(range 500000)))))
经过时间:674517.821669 msecs


To preface, I am on Windows 7 (64-bit), running Java version 6 (update 33) using clooj as my IDE. I have not tried to reproduce my problem in any other system. I am experienced with Clojure, but not at all with Java.

The entirety of the problem I am trying to solve is lengthy to describe, but it boils down to this: let's say I would like to a make a macro that takes one argument, an associative map, and returns a vector of the elements of the map with their order conserved.

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

That works, but add another element to the map and the order messes up...

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

I believe I have discovered why this is happening. It seems like anything with 8 or fewer elements is instantiated as a PersistentArrayMap, which is exactly what I want, because from what I can tell, this class retains order. However, anything with 9 or more elements is instantiated as a PersistentHashMap, which does not retain order.

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

I would like my macro to be able to take associative maps of any size, so this is a problem. I have tried type hinting, destructuring binding, for list comprehension, and unquote splicing, all without success. To draw it out, none of the following will work:

(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))

With this toy problem I present, I realize I could simply write my macro like so, and avoid the problem altogether:

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

However, for the actual problem I am trying to solve (which I have not gotten into), it is important (although not 100% necessary) that the macro be able to take associative maps. It seems like I'm looking for how to cast the argument into a PersistentArrayMap before anything has a chance to happen to it. There may be some other avenue to a solution that I am simply not considering or aware of.

I've researched the best I've known how and haven't found anything helpful yet. Does anybody have any thoughts/advice?

解决方案

you can make your map with array-map

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

or with a larger 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])

as a bonus you can avoid using a macro, which is useful because macros are not first-class and don't compose well*


a note on your first comment from the documentation on 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]

*this is my opinion


EDIT:

a time comparason of sorted-map vs. 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"

这篇关于Clojure宏将保存关联映射顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆