6

为什么会这样,

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 映射到 Unit 并且具有副作用的迭代器上的映射无法显示这些副作用?

编辑:

另外,如果迭代器是惰性的,为什么下面的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)
4

2 回答 2

7

因为迭代器上的 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永远不会被调用

于 2012-09-28T00:03:39.370 回答
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

现在我们有了一个新的迭代器。这将在访问数据时应用已映射的功能。但我们没有真正查看原始列表。

scala> mappedItr.next
1

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

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

scala> mappedItr.next
2

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

于 2012-09-28T00:03:48.613 回答