Scala迭代器上的map操作不会产生副作用。

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)

顺便说一下,您正在使用错误的 Iterable 扩展(所有扩展 Iterable 的类都应该提供“newBuilder”方法)。 - om-nom-nom
2个回答

7
由于迭代器上的cause 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时,显然你正在进行副作用,所以惰性是不期望的。但是我对map不会这样说。

更新

至于你的编辑:这种行为的原因是语句结果隐式调用toString,从而限制了迭代器--请自己尝试这段代码:

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

您会发现函数f从未被调用


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

再次强调,它只在需要时评估函数以便给我们最终结果


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接