Scala:结束Stream.iterate

时间:2018-04-17 17:26:01

标签: scala stream iteration stdin

经过一些反复试验后,我找到了一种结束Stream.iterate 的方法(如果标准输入在我的情况下结束)。但对我而言,它似乎更像是一种邪恶的黑客,而不是最佳实践解决方案。

之前未结束,如果标准输入因<{1}}无限运行而结束):

Stream.iterate

之后(现在按预期工作):

val initialDocument = Document()
val in: Stream[Document] = Stream.iterate(Stream[Document]()) { documents =>
  val lastDocument: Document = documents.lastOption.getOrElse(initialDocument)
  val line: String = io.StdIn.readLine
  if(line != null) {
    line
      .split(";")
      .map(_.trim)
      .scanLeft(lastDocument)((document: Document, line: String) => document.processInput(line))
      .drop(1) // drop the seed
      .toStream
  } else {
    Stream.empty
  }
}.flatten
for(document <- in) {
  // do something with the document snapshot
}

正如您所看到的,引入了几个新的val initialDocument = Document() val in: Stream[Document] = Stream.iterate(Stream[Option[Document]]()) { documents => val lastDocument: Option[Document] = Some(documents.lastOption.flatten.getOrElse(initialDocument)) val line: String = io.StdIn.readLine if(line != null) { line .split(";") .map(_.trim) .scanLeft(lastDocument)((document: Option[Document], line: String) => document.map(_.processInput(line))) .drop(1) // drop the seed .toStream } else { Stream(None) // "None" is used by "takeWhile" to see we have no more input } }.flatten.takeWhile(_.isDefined).map(_.get) for(document <- in) { // do something with the document snapshot } 类型值。他们唯一的目的是告诉Option是否达到目的。

我怎样才能以更优雅的形式编写此功能?

2 个答案:

答案 0 :(得分:2)

如果我理解你正在做的事情,这将以更简单的方式解决你的问题:

val in = Iterator
  .continually(io.StdIn.readLine())       // Read all lines from StdIn infinitely
  .takeWhile(_ != null)                   // Stop on EOI
  .flatMap(_.split(';'))                  // Iterator of sublines
  .map(_.trim)                            // Iterator of trimmed sublines
  .scanLeft(Document())(_ processInput _) // Iterator of a Document snapshot per subline
  .drop(1)                                // Drop the empty Document

for (document -> in) {
  // do something with the document snapshot
}

基本上,首先从整个输入创建一个惰性Iterator修剪线部分,然后根据此迭代器创建文档快照。

最好避免使用Stream,除非你真的需要它的记忆功能。 Stream速度很慢,并且memoization很容易导致内存泄漏。 Iterator具有创建有限或无限延迟序列的所有相同方法,并且应该是用于此目的的首选集合。

答案 1 :(得分:1)

我想知道事情是否变得有点复杂,Streams中的Streams和scanLeft()内的iterate()等等。投入Option类型以确定{{ 1}} - end有一种腥味。

Stream具有自然结束条件。我想知道这样的事情是否会更好。

Iterator

我不知道这是否真的有效。我没有class DocItr(private var prev :Document) extends Iterator[Document] { private var innerItr :Iterator[Document] = _ private var line :String = _ override def hasNext :Boolean = innerItr.hasNext || { line = io.StdIn.readLine Option(line).fold(false)(_.nonEmpty) } override def next() :Document = { if (!innerItr.hasNext) { innerItr = line.split(";") .map(_.trim) .scanLeft(prev)((doc: Document, in: String) => doc.processInput(in)) .drop(1) // drop the seed .toIterator } prev = innerItr.next() prev } } for(document <- new DocItr(initialDocument)) { // do something with the document snapshot } 类型可供使用。

我改变了&#34;继续&#34;从Documentline != null的条件,以便它将在任何空输入上结束,而不只是Option(line).fold(false)(_.nonEmpty)。它只是让测试变得更容易。