首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >into与partition

into与partition
EN

Stack Overflow用户
提问于 2018-01-27 11:54:23
回答 3查看 187关注 0票数 1

这是有意义的:

代码语言:javascript
复制
user=> (into {} [[:a 1] [:b 2]])
{:a 1, :b 2}

但是为什么这会产生一个错误呢?

代码语言:javascript
复制
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)

只是为了确认一下:

代码语言:javascript
复制
user=> (partition 2 [:a 1 :b 2])
((:a 1) (:b 2))

into对惰性序列有问题吗?如果有,原因何在?

除了解释这种方法不起作用的原因之外,推荐的将像[:a 1 :b 2]这样的键-值对序列组合到映射中的方法是什么?(apply conj似乎也不起作用。)

EN

回答 3

Stack Overflow用户

发布于 2018-01-27 11:59:27

您可以将序列applyassoc

代码语言:javascript
复制
(apply assoc {:foo 1} [:a 1 :b 2])
=> {:foo 1, :a 1, :b 2}

在懒惰序列方面有问题吗?如果有,原因何在?

不,into通常用于延迟评估的序列。这是惰性的,但每个键/值元组都是一个向量,这就是为什么当into减少映射中的对时,它会起作用:

代码语言:javascript
复制
(into {} (map vector (range 3) (repeat :x)))
=> {0 :x, 1 :x, 2 :x}

这不起作用,因为键/值对是列表:

代码语言:javascript
复制
(into {} (map list (range 3) (repeat :x)))

所以区别不是懒惰;这是因为into在地图上使用reduceconj,这只适用于向量键/值对(或MapEntry):

代码语言:javascript
复制
(conj {} [:a 1]) ;; ok
(conj {} (MapEntry. :a 1)) ;; ok
(conj {} '(:a 1)) ;; not ok

更新:assoc包装器,用于应用评论中建议的空/nil序列:

代码语言:javascript
复制
(defn assoc*
  ([m] m)
  ([m k v & kvs]
   (apply assoc m k v kvs)))
票数 4
EN

Stack Overflow用户

发布于 2018-01-27 12:05:25

推荐的方式-(假设seq arg是非空的,正如OP所指出的)-将是

代码语言:javascript
复制
Clojure 1.9.0
user=> (apply assoc {} [:a 1 :b 2])
{:a 1, :b 2}

带有partition的版本不起作用,因为partition返回的块是seqs,并且当conj'd以矢量和实际映射条目的方式添加到映射时,这些块不会被视为映射条目。

例如,(into {} (map vec) (partition 2 [:a 1 :b 2]))可以工作,因为这里在conjing之前将对转换为向量。

尽管如此,除非有一些特定的环境使得into很方便(例如,如果您有一堆传感器,希望用来预处理您的partition-generated对等),否则使用assoc的方法是更可取的。

票数 2
EN

Stack Overflow用户

发布于 2018-01-28 07:18:38

Clojure将诸如[:a 1]之类的2-vec视为等同于MapEntry,执行等同于“自动类型转换”的操作。我尽量避免这一点,而且总是很明确。

代码语言:javascript
复制
(first {:a 1})        => <#clojure.lang.MapEntry [:a 1]>
(conj {:a 1} [:b 2])  => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>

因此我们可以看到,MapEntry打印方式类似于向量,但具有不同的类型(就像Clojure seq打印方式类似于list,但具有不同的类型)。seq将Clojure map转换为一系列的MapEntry,而first会得到第一个(大多数Clojure函数在任何其他处理之前都会在任何输入集合上调用(seq ...) )。

请注意,conj执行反向类型转换,将向量 [:b 2]视为MapEntry。但是,conj不会为listseq执行自动类型转换

代码语言:javascript
复制
(throws? (conj {:a 1} '(:b 2)))
(throws? (into {:a 1} '(:b 2)))

intosince it is basically just (reduce conj <1st-arg> <2nd-seq>)中也有同样的问题。

其他答案已经有了3种工作方式:

代码语言:javascript
复制
(assoc {}   :b 2)             => {:b 2}
(conj  {}  [:b 2])            => {:b 2}
(into  {} [[:a 1] [:b 2]])    => {:a 1, :b 2}

然而,我会避免这些,并坚持使用hash-mapsorted-map,这两种方法都避免了输入序列为空的问题:

代码语言:javascript
复制
(apply hash-map [])           => {}            ; works for empty input seq
(apply hash-map [:a 1 :b 2])  => {:b 2, :a 1}

如果您的输入序列是一个配对列表,flatten有时会很有帮助:

代码语言:javascript
复制
(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

如果您已经有了一个映射,并且想要合并成一个(可能为空的)键值对序列,那么只需组合使用intohash-map

代码语言:javascript
复制
(into {:a 1} (apply hash-map []))       => {:a 1}
(into {:a 1} (apply hash-map [:b 2]))   => {:a 1, :b 2}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48472425

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档