【问题标题】:Why is a Range transformed to a Vector after map operation?为什么 Map 操作后 Range 会转换为 Vector?
【发布时间】:2012-10-19 06:55:44
【问题描述】:

在 Coursera 上的 Scala 课程之后,Martin Odersky 展示了一个示例代码:

1 to 5 map ( i => i*i )

他说Range 被转换为Vector,因为它们共享相同的接口 (IndexedSeq),结果无法表示为Range (在它的示例中更清楚,因为他生成了一对不能表示为 Range)。

我不确定是否理解,因为我认为他之前说过,在 for 表达式中,第一个生成器将确定将产生的元素类型,这似乎并不总是正确的,至少对于 Range 而言。

我不确定为什么输出是Vector,因为Vector 可能不是唯一可以表示上面计算结果的另一种实现。

有人可以帮我理解这部分吗?

【问题讨论】:

    标签: scala scala-collections


    【解决方案1】:

    map 秘密地将CanBuildFrom 作为隐含参数。它的工作是根据您已经拥有的集合(以及内容的类型)生成一个新集合。因为Range 不能包含任意内容——甚至是任意整数——所以没有CanBuildFrom 可以产生Range。具有CanBuildFromRange 的最具体超类型是IndexedSeq。这个实际构建的集合是Vector

    【讨论】:

    • 所以你的意思是Scala默认隐式导入一些隐式CanBuildFroms,我最终可以覆盖它们还是什么?在这种情况下,是否有可能产生除 Vector 之外的其他东西?
    • @SebastienLorber - 确实可以!寻找 breakOut 可能是最方便的方法,如果其他一些隐式已经可以适用,例如stackoverflow.com/questions/2592024
    • 是的,如果您明确提供有效的构建器,您可以获得另一个集合
    • 如果我们使用 CanBuildFrom 生成的东西不提供单子操作,这会是一个理解问题吗?
    【解决方案2】:

    我确信 Martin 也解释过,for 理解对应于(被翻译成)mapflatMap 方法的链式调用(如果你不使用 yield,则为 foreach) .

    它通常导致第一个生成器类型的值的原因是mapflatMap 通常返回与其接收器相同的类型(List 上的map 返回一个List等)。

    现在Ranges 的问题是它们不能表示不是常规整数序列的事物。因此,mapflatMap as defined for Range 的返回类型不能是Range。下一个最佳匹配是Vector,它是索引序列的原型实现。

    (如果您查看源代码甚至我链接到的 Scala 文档页面,您会发现它比仅返回类型要复杂一些,但从概念上讲,这就是原因。编辑: ...现在 Rex Kerr 刚刚投下了 CanBuildFrom 炸弹。)

    【讨论】:

      【解决方案3】:

      VectorIndexedSeq 的默认实现。 map 不能表示为 Range,因为 Range 类被设计为包含一系列可以由开始、停止和步长值表示的数字(类似于 Python 中的 range)。 API 文档指定它是 IndexSeq 的特例。

      我们可以看到1 to 5 map { i => i * i } 将为我们提供一个值容器(1、4、9、16、25)。我们可以得到一个开始和停止,但没有恒定的步长值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-03
        • 1970-01-01
        • 1970-01-01
        • 2013-01-24
        • 2022-01-22
        • 2022-01-24
        • 1970-01-01
        • 2014-09-13
        相关资源
        最近更新 更多