迭代器上的Scala贴图不会产生副作用

时间:2012-09-27 23:57:18

标签: scala map iterator

为什么会这样,

scala> List(1,2,3,4).iterator.map((x: Int) => println(x))

不打印

1
2
3
4

,而

List(1,2,3,4).map((x: Int) => println(x))
List(1,2,3,4).foreach((x: Int) => println(x))
List(1,2,3,4).iterator.foreach((x: Int) => println(x))

所有人都这样做?

换句话说,为什么迭代器上的地图将类型T映射到单位并且副作用无法显示这些副作用?

编辑:

另外,为什么如果迭代器是惰性的,下面的lazyMap调用实际上会从头到尾计算新的迭代器(提供完整的新迭代器)?

def lazyMap[T, U](coll: Iterable[T], f: T => U) = new Iterable[U] {
  def iterator = coll.iterator map f
}

scala> lazyMap(List(1,2,3,4), (x: Int) => x + 1)
res4: java.lang.Object with Iterable[Int] = (2, 3, 4, 5)

2 个答案:

答案 0 :(得分:7)

因为iterator上的map是懒惰的,你需要一些严格性:

scala> List(1,2,3,4).iterator.map((x: Int) => println(x))
res0: Iterator[Unit] = non-empty iterator

// nothing actually happened yet, just remember to do this printing things

scala> res0.toList
1
2
3
4
res1: List[Unit] = List((), (), (), ())

当你在迭代器上做foreach时很明显你正在做副作用,所以懒惰将是不受欢迎的。我不会这么说地图。

<强> UPD

至于你的编辑:这种行为的原因是,对于语句结果有一个toString的隐式调用,而这反过来又会严格破坏迭代器 - 自己尝试这个代码:

scala> { lazyMap(List(1,2,3,4), {(x: Int) => println(x); x + 1}); 1 }

您将看到函数f永远不会被调用

答案 1 :(得分:6)

迭代者的观点是懒惰。换句话说,当您创建迭代器时,在您去读取数据之前,它不会评估任何内容。这是看起来像:

scala> val itr = List(1,2,3).iterator
itr: Iterator[Int] = non-empty iterator

好的,我们现在有了一个迭代器。但它实际上还没有查看清单。

scala> val mappedItr = itr.map((x: Int) => println(x))
mappedItr: Iterator[Unit] = non-empty iterator

现在我们有了一个新的Iterator。当访问数据时,这将应用已映射的函数。但我们实际上没有查看原始列表。

scala> mappedItr.next
1

这是我们第一次访问数据,因此这是Iterator第一次查看列表。我们打电话给next,所以我们得到了第一个元素。由于我们的迭代器排队map,因此当我们访问该元素时它会应用映射函数。所以我们看到函数的结果应用于next项。

我们可以再做一次以获得下一个元素:

scala> mappedItr.next
2

而且,它仅在需要时评估函数,以便为我们提供最终结果。