Scala:从stdin读取时扫描左后一项

时间:2018-03-21 22:47:42

标签: scala iterator

如果我使用stdin处理来自scanLeft的输入,则结果输出总是在我上一次输入后面一行:

io.Source.stdin
  .getLines
  .scanLeft("START:")((accu, line) => accu + " " + line)
  .foreach(println(_))

结果(我的手动输入前面有>):

> first
START:
> second
START: first
> third
START: first second

我想要的合理输出是:

> first
START: first
> second
START: first second
> third
START: first second third

如您所见,第一个输入行后面的输出应该已经包含第一个输入行的字符串。

我已经使用.scanLeft(...).drop(1).foreach(...)尝试过了,但这导致了以下结果:

> first
> second
START: first
> third
START: first second

如何正确省略纯种子以获得所需结果?

[UPDATE] 目前,我很满意安德烈泰金的精彩解决方法。非常感谢你的建议。

但是,当然,如果有scanLeft的替代品没有将种子作为第一项发送到下一个迭代链中,我会更喜欢这个解决方案。

[UPDATE]

用户 jwvh 了解我的目标并提供了一个出色的解决方案。为了完善他们的建议,我寻求一种预处理线路的方法,然后再将它们发送到累积回调中。因此,readLine命令不应该在累积回调中调用,而是在我可以添加的不同链链接中调用。

2 个答案:

答案 0 :(得分:5)

编辑摘要:添加了map,以证明getLines返回的行的预处理同样重要。

您可以将println移动到scanLeft本身,强制立即执行而不会出现延迟:

io.Source.stdin
  .getLines
  .scanLeft("START:") {
    (accu, line) => accu + " " + line
    val res = accu + " " + line
    println(res)
    res
  }.foreach{_ => }

然而,这似乎与更短更直观的foldLeft

完全相同
io.Source.stdin
  .getLines
  .foldLeft("START:") {
    (accu, line) => accu + " " + line
    val res = accu + " " + line
    println(res)
    res
  }

互动示例:

first
START: first
second
START: first second
third
START: first second third
fourth
START: first second third fourth
fifth
START: first second third fourth fifth
sixth
START: first second third fourth fifth sixth
seventh
START: first second third fourth fifth sixth seventh
end
START: first second third fourth fifth sixth seventh end

编辑

您当然可以添加map步骤来预处理这些行:

io.Source.stdin
  .getLines
  .map(_.toUpperCase)
  .foldLeft("START:") {
    (accu, line) => accu + " " + line
    val res = accu + " " + line
    println(res)
    res
  }

示例交互(键入小写,打印大写):

> foo
START: FOO
> bar
START: FOO BAR
> baz
START: FOO BAR BAZ

答案 1 :(得分:3)

您可以使用Stream.iterate()代替scanLeft()StdIn.readLine代替stdin.getLines获得非常相似的内容。

def input = Stream.iterate("START:"){prev =>
  val next = s"$prev ${io.StdIn.readLine}"
  println(next)
  next
}

由于Stream被懒惰地评估,你需要一些方法来实现它。

val inStr = input.takeWhile(! _.contains("quit")).last
START: one                //after input "one"<return>
START: one two            //after input "two"<return>
START: one two brit       //after input "brit"<return>
START: one two brit quit  //after input "quit"<return>
//inStr: String = START: one two brit

如果这是一个要求,你实际上不必放弃getLines迭代器。

def inItr = io.Source.stdin.getLines

def input = Stream.iterate("START:"){prev =>
  val next = s"$prev ${inItr.next}"
  println(next)
  next
}

不确定这是否符合您的意见。很多情况取决于可能出现的错误以及确定错误的方式。

Stream.iterate(document()){ doc =>
  val line = io.StdIn.readLine  //blocks here
                     .trim
                     .filterNot(_.isControl)
                     //other String or Char manipulations
  doc.update(line)
  /* at this point you have both input line and updated document to play with */
  ... //handle error and logging requirements
  doc //for the next iteration
}

我假设.update()修改源文档并且不返回任何内容(返回Unit)。这是update()方法的常用签名。

大部分内容都可以在调用链中完成(_.method1.method2.等),但有时这会让事情变得更复杂。

通过使用名为kestrel pattern的内容,仍然可以将不返回感兴趣值的方法添加到调用链中。