Clojure Maps,创建键值的顺序

时间:2013-08-27 02:45:43

标签: map clojure parallel-processing

clojure profiling library中,有一个存储一些分析信息的原子,它由许多函数修改。在评论中,他们声称为了提高效率,最后调用摘要统计信息(其中一个函数),但我无法看到它是如何在实际代码中强制执行的。

然后我注意到这可以强制执行的一种方式是构造地图的顺序,所以我重新创建了一个更简单的例子来演示。

(defn t []
  (let [a (atom {})]
    {:b (swap! a #(assoc % :data 'is-here))
     :c (keys @a)}))

在这里,与评论一致,代码中的哪一个:b或:c首先将确定a的值。当然必须有一些排序,但这种行为是否得到保证?看起来它不应该是,因为无序散列没有排序,这可能对并行性产生不利影响。

2 个答案:

答案 0 :(得分:3)

我会考虑地图文字的评估顺序是一个实现细节,并在地图文字中使用带有副作用的非常量表达式(例如设置一个原子状态)来寻找麻烦。

对于定义良好的评估语义,使用函数调用,其中保证从左到右计算参数。

(let [i (atom 0)] 
  (hash-map :a (swap! i inc) :b (swap! i inc) :c (swap! i inc)))
;=> {:a 1, :c 3, :b 2}

请注意,因为我们使用了hash-map,所以键是乱序的,但是这些值对应于所写顺序中的键。

实施细节

地图文字的实现细节(版本1.5)取决于几个方面:

  • 读者将未评估的地图文字视为小地图(8对或更少)的有序数组映射或较大地图的无序散列映射。

    < / LI>
  • 如果键和值是常量表达式,编译器评估程序将读取器提供的映射表达式解析为无序散列映射,而不考虑大小,但会将其作为数组映射进行评估。否则,编译器评估程序将根据读者提供的密钥顺序进行评估。

一个小例子可以让它更清晰一些(或许):

user=> {:a 1, :b 2, :c 3}
{:a 1, :c 3, :b 2}
user=> (type *1)
clojure.lang.PersistentArrayMap

user=> (def m1 {:a 1, :b 2, :c 3})
#'user/m1
user=> m1
{:a 1, :c 3, :b 2}
user=> (type m1)
clojure.lang.PersistentHashMap
user=> (eval m1)
{:a 1, :c 3, :b 2}
user=> (type *1)
clojure.lang.PersistentArrayMap

但是...

user=> (def m2 {:a ((fn [] 1)), :b 2, :c 3})
#'user/m2
user=> m2
{:a 1, :b 2, :c 3}
user=> (type m2)
clojure.lang.PersistentArrayMap

答案 1 :(得分:2)

我相信,只要{...}形式保持等于或小于8个地图条目(键/值对),订单就会保持不变。 {...}将成为包含8个或更少条目的PersistentArrayMap,否则将成为PersistentHashMap。前者将保留其映射条目的给定顺序,保证不扩展到后者。 Source

至于并行性,我认为以下简单测试表明,对于使用8个或更少条目创建的地图,将按照给定的顺序评估地图条目:

user=> (let [x (atom 0)]
         {:a (do (Thread/sleep 1000) (swap! x inc))
          :b (swap! x inc)})
{:a 1, :b 2}

但是可以用其他顺序评估创建的条目超过8个:

user=> (let [x (atom 0)]
         {:a (do (Thread/sleep 1000) (swap! x inc))
          :b (swap! x inc)
          :c (swap! x inc)
          :d (swap! x inc)
          :e (swap! x inc)
          :f (swap! x inc)
          :g (swap! x inc)
          :h (swap! x inc)
          :i (swap! x inc)})
{:a 1, :c 2, :b 3, :f 4, :g 5, :d 6, :e 7, :i 8, :h 9}

尽管如此,至少对于9个条目的哈希映射,似乎并不是在他们的瞬间进行任何并行性。这很有趣。