Clojure 进入与分割
这是有道理的:Clojure 进入与分割,clojure,lazy-evaluation,Clojure,Lazy Evaluation,这是有道理的: user=> (into {} [[:a 1] [:b 2]]) {:a 1, :b 2} 但这为什么会产生错误呢 user=> (into {} (partition 2 [:a 1 :b 2])) ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry clojure.lang.ATransientMap.conj (ATransientMap.java:44
user=> (into {} [[:a 1] [:b 2]])
{:a 1, :b 2}
但这为什么会产生错误呢
user=> (into {} (partition 2 [:a 1 :b 2]))
ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry clojure.lang.ATransientMap.conj (ATransientMap.java:44)
可以肯定的是:
user=> (partition 2 [:a 1 :b 2])
((:a 1) (:b 2))
into
是否存在惰性序列问题?若然,原因为何
除了解释这不起作用的原因之外,建议使用什么方法将诸如
[:a1:b2]
之类的键值对序列连接到映射中?(apply conj
似乎也不起作用。)您可以apply
将序列应用到assoc
:
(apply assoc {:foo 1} [:a 1 :b 2])
=> {:foo 1, :a 1, :b 2}
into对惰性序列有问题吗?若然,原因为何
否,into
通常用于延迟求值序列。这是惰性的,但每个键/值元组都是一个向量,这就是为什么当into
减少映射中的对时,它会起作用:
(into {} (map vector (range 3) (repeat :x)))
=> {0 :x, 1 :x, 2 :x}
这不起作用,因为键/值对是列表:
(into {} (map list (range 3) (repeat :x)))
所以区别不在于懒惰;这是由于在地图上使用conj
将转换为使用reduce
造成的,它只适用于向量键/值对(或MapEntry
s):
更新:assoc
wrapper,用于应用注释中建议的空/nil序列:
(defn assoc*
([m] m)
([m k v & kvs]
(apply assoc m k v kvs)))
您可以将序列应用于关联:
(apply assoc {:foo 1} [:a 1 :b 2])
=> {:foo 1, :a 1, :b 2}
into对惰性序列有问题吗?若然,原因为何
否,into
通常用于延迟求值序列。这是惰性的,但每个键/值元组都是一个向量,这就是为什么当into
减少映射中的对时,它会起作用:
(into {} (map vector (range 3) (repeat :x)))
=> {0 :x, 1 :x, 2 :x}
这不起作用,因为键/值对是列表:
(into {} (map list (range 3) (repeat :x)))
所以区别不在于懒惰;这是由于在地图上使用conj
将转换为使用reduce
造成的,它只适用于向量键/值对(或MapEntry
s):
更新:assoc
wrapper,用于应用注释中建议的空/nil序列:
(defn assoc*
([m] m)
([m k v & kvs]
(apply assoc m k v kvs)))
建议的方法是(假设seq arg为非空,正如OP所指出的那样)
Clojure 1.9.0
user=> (apply assoc {} [:a 1 :b 2])
{:a 1, :b 2}
带有分区
的版本不起作用,因为分区
返回的块是seqs,当conj
以向量和实际地图条目的方式绘制地图时,这些块不被视为地图条目
例如,(转换为{}(map-vec)(分区2[:a1:b2])
会起作用,因为在这里,对在连接之前被转换为向量
尽管如此,使用assoc
的方法还是比较可取的,除非有一些特殊情况使得变成很方便(比如,如果你有一堆传感器,你想用来预处理你的分区
生成的对等)。推荐的方法——(假设seq arg非空,如OP所指出的)–将
Clojure 1.9.0
user=> (apply assoc {} [:a 1 :b 2])
{:a 1, :b 2}
带有分区
的版本不起作用,因为分区
返回的块是seqs,当conj
以向量和实际地图条目的方式绘制地图时,这些块不被视为地图条目
例如,(转换为{}(map-vec)(分区2[:a1:b2])
会起作用,因为在这里,对在连接之前被转换为向量
尽管如此,使用assoc
的方法还是比较可取的,除非有一些特殊情况使得变成方便(比如,如果你有一堆传感器,你想用来预处理你的分区
生成的对等)。Clojure处理一个2-vec,比如[:a1]
相当于一个MapEntry
,执行相当于“自动类型转换”的操作。我尽量避免这种情况,并始终保持显式
(first {:a 1}) => <#clojure.lang.MapEntry [:a 1]>
(conj {:a 1} [:b 2]) => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>
into
也有同样的问题(reduce conj)
其他答案已经有3种可行的方法:
(assoc {} :b 2) => {:b 2}
(conj {} [:b 2]) => {:b 2}
(into {} [[:a 1] [:b 2]]) => {:a 1, :b 2}
但是,我会避免这些,并坚持使用哈希映射
或排序映射
,这两种方法都避免了空输入序列的问题:
(apply hash-map []) => {} ; works for empty input seq
(apply hash-map [:a 1 :b 2]) => {:b 2, :a 1}
如果您的输入序列是成对的列表,flatte
有时很有用:
(apply sorted-map (flatten [[:a 1] [:b 2]])) => {:a 1, :b 2}
(apply hash-map (flatten '((:a 1) (:b 2)))) => {:a 1, :b 2}
附笔。
请注意,这些是不同的:
- java.util.Map$Entry(在jdk文档中列为“Map.Entry”)
- clojure.lang.MapEntry
P.P.S
如果您已经有一个映射,并且希望合并成一个键值对序列(可能是空的),只需将合并到
和哈希映射中即可:
(into {:a 1} (apply hash-map [])) => {:a 1}
(into {:a 1} (apply hash-map [:b 2])) => {:a 1, :b 2}
Clojure将2-vec(如[:a1]
)视为等同于MapEntry
,执行相当于“自动类型转换”的操作。我试图避免这种情况,并始终明确表示
(first {:a 1}) => <#clojure.lang.MapEntry [:a 1]>
(conj {:a 1} [:b 2]) => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>
into
也有同样的问题(reduce conj)
其他答案已经有3种可行的方法:
(assoc {} :b 2) => {:b 2}
(conj {} [:b 2]) => {:b 2}
(into {} [[:a 1] [:b 2]]) => {:a 1, :b 2}
但是,我会避免这些,并坚持使用哈希映射
或排序映射
,这两种方法都避免了空输入序列的问题:
(apply hash-map []) => {} ; works for empty input seq
(apply hash-map [:a 1 :b 2]) => {:b 2, :a 1}
如果您的输入序列是成对的列表,flatte
有时很有用:
(apply sorted-map (flatten [[:a 1] [:b 2]])) => {:a 1, :b 2}
(apply hash-map (flatten '((:a 1) (:b 2)))) => {:a 1, :b 2}
附笔。
请注意,这些是不同的:
- java.util.Map$Entry(在jdk文档中列为“Map.Entry”)
- clojure.lang.MapEntry
P.P.S
如果您已经有一个映射,并且希望合并成一个键值对序列(可能是空的),只需将合并到
和哈希映射中即可:
(into {:a 1} (apply hash-map [])) => {:a 1}
(into {:a 1} (apply hash-map [:b 2])) => {:a 1, :b 2}
成功了!谢谢。我很想看到一个解释,解释为什么into
失败和assoc
工作。通常Clojure的行为和预期的一样;我想知道我需要知道什么才能“预期”这个。我刚刚发现(应用assoc{}序列)如果序列
为空,则
失败。在这种情况下,您可以在另一个答案中使用传感器版本:(进入{}(映射向量)(分区2[]))
或者如果你发现自己经常这样做,你可以引入一个附加一元重载的助手包装assoc
,返回地图。如果只是一个或两个地方,那么if
可能是最简单的方法。这很有效!谢谢。我很乐意看到