2

这是我的代码

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 理解的屈服值更改为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))

我的问题是确定结果类型的机制是什么?

4

2 回答 2

2

当您查看 API 文档以了解 map-Method 的详细信息时,您会发现它有第二个隐式参数 type CanBuildFrom。from的一个实例CanBuildFrom定义了在映射到某个其他集合并提供某种元素类型时如何构建某个集合。

在您获得 Map 作为结果的情况下,您正在映射 Map 并提供二进制元组。所以编译器搜索一个CanBuildFrom-instance,它可以处理它。

为了找到这样的实例,编译器在不同的地方查找,例如当前作用域、调用方法的类及其伴生对象。在这种情况下,它将找到一个canBuildFrom在其伴生对象中调用的隐式字段,该字段Map是合适的并且可用于构建Map结果。因此,它尝试将结果类型推断为,Map并在成功时使用此实例。

在您提供单个值或三元组的情况下,在伴随的实例中找到的实例Map不具有所需的类型,因此它会继续向上搜索继承树。它在 的伴生对象中找到它Iterable。他们允许构建Iterable任意元素类型的实例。所以编译器使用它。

那你为什么会得到一个List?因为那恰好是那里使用的实现,所以类型系统只保证你一个Iterable.

如果您想获得 anIterable而不是 aMap您可以CanBuildFrom显式提供一个实例(仅当您直接调用 map 和 flatMap 时)或只强制返回类型。List在那里,您还会注意到,即使您得到一个,您也无法请求。

这行不通:

val l: List[Int] = Map(1->2).map(x=>3)

然而,这将:

val l: Iterable[Int] = Map(1->2).map(x=>3)
于 2016-01-31T18:38:29.570 回答
0

要添加到@dth,如果你想要一个列表,你可以这样做:

val l = Map(1->2,3->4).view.map( ... ).toList

这里的 map 函数应用在一个lazy 上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))

在这里,省略.viewmake map 输出一个覆盖重复键的 Map (并做额外的和不必要的工作)。

于 2016-04-15T14:00:10.483 回答