简体   繁体   English

Clojure Maps,创建键值的顺序

[英]Clojure Maps, order of key value creation

In a clojure profiling library , there is a an atom storing some profiling information and it is modified by a number of functions. clojure分析库中 ,有一个存储一些分析信息的原子,它由许多函数修改。 In the comments, they assert that the summary statistics (one of the functions) is called last for efficiency purposes, but I could not see how this was enforced in the actual code. 在评论中,他们断言,为了提高效率,最后一次调用摘要统计信息(其中一个函数),但我无法看到它是如何在实际代码中强制执行的。

I then noticed that one manner in which this could enforced is the ordering in the construction of a map, so I recreated a simpler example to demonstrate. 然后我注意到,这可以强制执行的一种方式是构造地图的顺序,所以我重新创建了一个更简单的示例来演示。

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

Here, in agreement with the comment, which one of :b or :c comes first in the code will determine the value of a. 在这里,与评论一致,代码中的哪一个:b或:c首先将确定a的值。 Of course there must be some ordering, but is this behaviour guaranteed? 当然必须有一些排序,但这种行为是否得到保证? It seems like it should not be, since an unordered hash has no ordering, and this could have detrimental implications for parallelism. 它似乎不应该,因为无序散列没有排序,这可能对并行性有不利影响。

I would consider the evaluation order of map literals an implementation detail and using non-constant expressions with side effects (eg setting an atom state) in map literals to be asking for trouble. 我会考虑地图文字的评估顺序是一个实现细节,并在地图文字中使用带有副作用的非常量表达式(例如设置一个原子状态)来寻找麻烦。

For well defined evaluation semantics, use a function call, where the arguments are guaranteed to be evaluated from left to right. 对于定义良好的评估语义,使用函数调用,其中保证从左到右计算参数。

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

Note that the keys are out of order since we used hash-map, but the values correspond to the keys in the order written. 请注意,由于我们使用了哈希映射,因此键是乱序的,但这些值对应于写入顺序中的键。

Implementation details 实施细节

The implementation details of map literals (in version 1.5) depends on several things: 映射文字的实现细节(在1.5版中)取决于几个方面:

  • The reader treats the unevaluated map literals as an ordered array-map for small maps (8 pairs or fewer) or an unordered hash-map for larger maps. 读者将未评估的地图文字视为小地图(8对或更少)的有序数组映射或较大地图的无序散列映射。

  • The compiler-evaluator parses the map expression provided by the reader as an unordered hash-map irrespective of size if the keys and values are constant expressions, but will evaluate it as an array-map. 如果键和值是常量表达式,则编译器评估器将读取器提供的映射表达式解析为无序散列映射,而不考虑大小,但将其评估为数组映射。 Otherwise, the compiler-evaluator evaluates in the key order provided by the reader, dependent on size. 否则,编译器评估程序将根据大小评估读取器提供的键顺序。

An small example will make this a bit clearer (perhaps): 一个小例子可以使这一点更清晰(也许):

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

But... 但...

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

I believe, as long as the {...} form remains equal to or less than 8 map entires (key/value pairs), the order will be maintained. 我相信,只要{...}形式保持等于或小于8个地图条目(键/值对),订单就会保持不变。 A {...} will become a PersistentArrayMap with 8 or fewer entries, and a PersistentHashMap otherwise. 一个{...}将成为PersistentArrayMap用8个或更少的条目,以及PersistentHashMap否则。 The former will preserve the given order of its map entries, a guarantee not extended to the latter. 前者将保留其映射条目的给定顺序,保证不扩展到后者。 Source . 来源

As for parallelism, I think the following simple test shows that map entries will get evaluated in the order they are given, for maps created with 8 or fewer entries: 至于并行性,我认为以下简单的测试表明,对于使用8个或更少条目创建的地图,将按照给定的顺序对映射条目进行评估:

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

But may evaluate in some other order for maps created with greater than 8 entries: 但是,对于使用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}

Though, at least for hash-maps at 9 entries, it doesn't seem like any parallelism is going on during their instantation. 但是,至少对于9个条目的哈希映射,它们似乎并不是在它们的瞬间进行任何并行性。 That's interesting. 那很有意思。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM