这是我的代码
def testMap() = {
val x = Map(
1 -> Map(
2 -> 3,
3 -> 4
),
5 -> Map(
6 -> 7,
7 -> 8
)
)
for {
(a, v) <- x
(b, c) <- v
} yield {
a
}
}
上面的代码给出了
List(1, 1, 5, 5)
如果我将for comprehension a
的屈服值更改为(a, b)
,结果为
Map(1 -> 3, 5 -> 7)
如果我将(a, b)
更改为(a, b, c)
,则结果为
List((1,2,3), (1,3,4), (5,6,7), (5,7,8))
我的问题是在理解中确定结果类型背后的机制是什么?
答案 0 :(得分:2)
当您查看API文档的详细信息时,您将会发现它具有第二个隐式参数CanBuildFrom
。
CanBuildFrom
的一个实例定义了在映射某些其他集合时如何构建某个集合,并提供了某种元素类型。
如果您获得了Map作为结果,那么您将映射到Map并提供二进制元组。所以编译器会搜索一个CanBuildFrom
- 实例,它可以处理它。
为了找到这样的实例,编译器在不同的地方查找,例如当前范围,调用方法的类及其伴随对象。
在这种情况下,它将在canBuildFrom
的伴随对象中找到一个名为Map
的隐式字段,该字段是合适的,可用于构建Map
作为结果。因此它尝试将结果类型推断为Map
,并且成功使用此实例。
在您提供单个值或三元组的情况下,Map
的伴随中找到的实例没有所需的类型,因此它继续搜索继承树。它在Iterable
的伴随对象中找到它。它们的实例允许构建任意元素类型的Iterable
。所以编译器使用它。
那你为什么得到List
?因为恰好是那里使用的实现,类型系统只保证Iterable
。
如果您想获得Iterable
而不是Map
,则可以显式提供CanBuildFrom
实例(仅当您直接调用map和flatMap时)或强制返回类型。在那里,您还会注意到,即使您有List
,也无法申请val l: List[Int] = Map(1->2).map(x=>3)
。
这不起作用:
val l: Iterable[Int] = Map(1->2).map(x=>3)
然而,这将:
v1 = null;
答案 1 :(得分:0)
要添加到@dth,如果需要列表,可以执行以下操作:
class foo:
the_int = 0
def __init__(self, i):
self.bar = i
one, two = foo(5), foo(10)
ar = array([one, two])
int_array = ar[0:2].the_int
#I want int_array = [5, 10]
此处地图函数适用于惰性val l = Map(1->2,3->4).view.map( ... ).toList
,其输出IterableView
,实际构造由IterableView
触发。
注意:另外,不使用toList
会导致危险行为。例如:
view
在这里,省略val m = Map(2->2,3->3)
val l = m.map{ case (k,v) => (k/2,v) ).toList
// List((1,3))
val l = m.view.map{ case (k,v) => (k/2,v) ).toList
// List((1,2), (1,3))
使地图输出一个覆盖重复键的Map(并进行额外的和不必要的工作)。