在Coursera上的Scala课程之后,Martin Odersky展示了一个示例代码:
1 to 5 map ( i => i*i )
他说{{1}}转换为Range
,因为它们共享相同的界面(Vector
),结果无法表示为IndexedSeq
(在它的例子中更清楚,因为他生成了一对不能表示为Range
)的对。
我不确定理解,因为我认为他之前说过,在for表达式中,第一个生成器将确定将要生成的元素类型,并且似乎并非总是如此,至少Range
而且我不确定为什么输出是Range
,因为Vector
可能不是唯一可以代表上面计算结果的其他实现。
有人可以帮我理解这部分吗?
答案 0 :(得分:10)
map
秘密地将CanBuildFrom
作为隐含参数。它的工作是根据您已经获得的新系列(以及内容的类型)生成新的系列。由于Range
不能包含任意内容 - 甚至不包含任意整数 - 因此没有CanBuildFrom
生成Range
。具有Range
的{{1}}的最具体的超类型是CanBuildFrom
。实际构建的集合是IndexedSeq
。
答案 1 :(得分:2)
正如我相信马丁还解释的那样,for
理解对应于(被翻译成)map
和flatMap
方法的链式调用(以及foreach
如果你不要使用yield
)。
它通常导致第一个生成器的类型值的原因是map
和flatMap
通常返回与其接收器相同的类型(map
在{{ 1}}返回List
等。)。
现在List
的问题是它们不能代表不是常规整数序列的东西。因此,Range
和map
as defined for Range
的返回类型不能为flatMap
。下一个最佳匹配是Range
,即索引序列的原型实现。
(如果你看一下我链接到的源代码甚至是Scala文档页面,你会发现返回类型有点复杂,但从概念上讲,这就是原因。 编辑: ...现在雷克斯克尔刚刚放弃了Vector
炸弹。)
答案 2 :(得分:1)
Vector
是IndexedSeq
的默认实现。 map
无法表示为Range
,因为Range
类包含一系列可由开始,停止和步骤值表示的数字(类似于{{ Python中的1}}。 API文档指定它是range
的特殊情况。
我们可以看到IndexSeq
将为我们提供一个容器值(1,4,9,16,25)。我们可以得到一个开始和停止,但没有恒定的步长值。