为什么`Source.fromFile(...)。getLines()`在我迭代之后是空的?

时间:2013-03-19 14:49:24

标签: scala side-effects

令我非常惊讶的是(线< - lines)是如此具有破坏性!它完全展开了行迭代器。因此,运行以下代码段将使size = 0:

  val lines = Source.fromFile(args(0)).getLines()
  var cnt = 0
  for (line <- lines) {
    cnt = readLines(line, cnt)
  }
  val size = lines.size

像这样的隐藏副作用是否是正常的Scala练习?

2 个答案:

答案 0 :(得分:5)

Source.getLines()返回一个迭代器。对于每个迭代器,如果您调用上面的foreachmaptaketoList等批量操作,那么迭代器不再可用州。 这是Iterators的合同,更一般地说,是继承TraversableOnce的类。

  

特别重要的是要注意,除非另有说明,否则在调用方法之后永远不应使用迭代器。两个最重要的例外也是唯一的抽象方法:next和hasNext。

继承Traversable的类不是这种情况 - 对于那些可以根据需要多次调用批量遍历操作的类。

答案 1 :(得分:3)

Source.getLines()会返回Iterator,并且Iterator中的for将会改变它。这在Scala documentation

中非常清楚
  

迭代器是可变的:它上面的大多数操作都会改变它的状态。虽然它通常用于遍历集合的元素,但它也可以在没有任何集合支持的情况下使用(请参阅伴随对象上的构造函数)。

     

特别重要的是要注意,除非另有说明,否则在调用方法之后永远不应使用迭代器。两个最重要的例外也是唯一的抽象方法:next和hasNext。

使用map表示法只是在Iterator上调用flatMapforeach和{{1}}方法的语法糖,这些方法再次有明确的文档说明不使用迭代器:

  

重用:在调用此方法之后,应该丢弃它被调用的迭代器,并仅使用返回的迭代器。使用旧的迭代器是未定义的,可能会发生变化,并且可能导致对新迭代器的更改。

Scala通常旨在成为一种“实用”语言 - 虽然不鼓励,但出于性能和互操作性原因,允许出现突变和副作用。然而,将其称为“隐藏得很好”是一种延伸。