【问题标题】:Is there any method that does the same thing as map() but generates a different type of container?是否有任何方法与 map() 做同样的事情但生成不同类型的容器?
【发布时间】:2013-02-18 18:09:46
【问题描述】:

有时我需要通过映射另一个不同类型的集合来创建一个集合。例如,某些函数需要 List[_] 作为其参数类型,但我需要通过映射 IndexedSeq[_] 来生成它:

val r = (1 to n).map { ... }
someFunction(r.toList)

虽然我可以通过先调用IndexedSeq[_] 的map 方法然后再调用toList 来实现,但这会产生一个冗余的中间集合。有什么办法可以避免这个多余的步骤,同时保持代码简洁?

【问题讨论】:

标签: scala scala-collections


【解决方案1】:

查看map 的完整签名:

def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That

其中的关键是隐含的CanBuildFrom,它控制如何从输入集合生成结果集合。我们可以将隐含的CanBuildFrom 替换为显式的CanBuildFrom,这样我们就可以构建不同的结果集合。

更好的是,我们甚至不必编写这个显式方法!它已经以scala.collection.breakOut 的形式存在。来自 ScalaDoc:

提供一个 CanBuildFrom 实例,该实例构建特定目标集合 (To'),而与原始集合 (From') 无关。

所以如果我们传入collection.breakOut,我们就可以准确地指定我们想要的map方法:

val x = IndexedSeq(1,2,3,4,5)
x.map[Int, List[Int]](_ * 2)(collection.breakOut)
> res6: List[Int] = List(2, 4, 6, 8, 10) 

【讨论】:

  • 非常感谢您非常明确的回答。 Scala 的隐式参数是如此强大和神秘。我刚刚阅读了that 并学到了很多东西。
【解决方案2】:

正如 om-nom-nom 在他的评论中所说,您的问题的答案是 collection.breakOut。

breakOut 是给 map 方法的附加参数,并且 - 为了简单起见 - 它允许从 map 返回不同类型的集合:

def directMapExample(seq: Seq[Int]): Set[Int] = seq.map(_ * 2)(collection.breakOut)

请注意,以下内容在编译时失败:

def directMapExample2(seq: Seq[Int]): Set[Int] = seq.map(_ * 2)

详情请见https://stackoverflow.com/a/1716558/298389。

【讨论】:

  • 感谢您简洁的回答。
【解决方案3】:

使用视图可能会有所帮助?

val r = (1 to n).view.map { … }
someFunction(r.toList)

map 函数是Range 上的严格转换器。然而,如果你先把它变成一个视图,那么Range(这是一个非严格的集合)将被包装在一个以非严格方式实现map的对象中。只有在调用 toList 时才会产生完整的值范围。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多