5

If I process the input from stdin with scanLeft, the resulting output is always one line behind my last input:

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

Results in (my manual inputs are preceded by >):

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

The sensible output I want is:

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

As you can see, the output following the first input line should already contain the string of the first input line.

I already tried it using .scanLeft(...).drop(1).foreach(...), but this leads to the following result:

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

How do I correctly omit the pure seed to get the desired result?

[UPDATE] For the time being I am content with Andrey Tyukin's nifty workaround. Many thanks for suggesting it.

But of course, if there is any alternative to scanLeft that does not send the seed as first item into the following iteration chain, I will prefer that solution.

[UPDATE]

User jwvh understood my objective and provided an excellent solution to it. To round off their suggestion I seek a way of preprocessing the lines before sending them into the accumulation callback. Thus the readLine command should not be called in the accumulation callback but in a different chain link I can prepend.

ideaboxer
  • 3,863
  • 8
  • 43
  • 62

2 Answers2

4

Edit Summary: Added a map to demonstrate that the preprocessing of lines returned by getLines is just as trivial.


You could move println into the body of scanLeft itself, to force immediate execution without the lag:

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

However, this seems to behave exactly the same as a shorter and more intuitive foldLeft:

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

Example interaction:

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

EDIT

You can of course add a map-step to preprocess the lines:

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

Example interaction (typed lowercase, printed uppercase):

> foo
START: FOO
> bar
START: FOO BAR
> baz
START: FOO BAR BAZ
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • is the .forEach{_ => } needed? – Jordan Cutler Mar 22 '18 at 00:20
  • @JordanCutler it won't do anything otherwise, because it's lazy... It's only to force the execution. Maybe there is some better suited "force"-method, I'm not sure. – Andrey Tyukin Mar 22 '18 at 00:21
  • I tried it out and you are correct. It just ends the program when the .forEach{_ => } is not there, but why? What is lazy and why does it try to read my stdin input only when the forEach is there? – Jordan Cutler Mar 22 '18 at 00:29
  • @JordanCutler "lazy" means: executed only when it's needed. If it's not needed at all, it's never executed. Read up on what the actual difference between lists and streams is. Maybe learn you a haskell for great good, this is known to eliminate such questions rather effectively, even if you don't use it too often practically... ;) – Andrey Tyukin Mar 22 '18 at 00:36
  • I know what lazy means. I meant what is being executed lazily and why does it keep asking for input after the first line is entered (essentially making any part of the program below the scanLeft stuff unreachable, unless there is some way to break out of it reading input and move on to the rest of the program, like Ctrl+c) – Jordan Cutler Mar 22 '18 at 00:41
  • 1
    @JordanCutler It's not unreachable. Add a `println` after the stream, start it, make a few inputs, press Ctrl+D, it will print the stuff in `println`, and only then exit (I can speak only for linux/bash). I cannot claim that I find the whole lazy execution story totally 100% intuitive, but I'm not sure what exactly you don't understand. Is there a problem with my answer, or should it be a separate question? – Andrey Tyukin Mar 22 '18 at 00:48
  • Nah nothing wrong with your answer I was just asking follow up questions for understanding – Jordan Cutler Mar 22 '18 at 00:49
  • @ideaboxer I've added a hopefully less counter-intuitive variant with `foldLeft`, I hope that solves it. – Andrey Tyukin Mar 22 '18 at 18:29
3

You can get something pretty similar with Stream.iterate() in place of scanLeft() and StdIn.readLine in place of stdin.getLines.

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

Since a Stream is evaluated lazily you'll need some means to materialize it.

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

You actually don't have to give up on the getLines iterator if that's a requirement.

def inItr = io.Source.stdin.getLines

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

Not sure if this addresses your comments or not. Lots depends on where possible errors might come from and how they are determined.

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
}

I've assumed that .update() modifies the source document and returns nothing (returns Unit). That's the usual signature for an update() method.

Much of this can be done in a call chain (_.method1.method2. etc.) but sometimes that just makes things more complicated.

Methods that don't return a value of interest can still be added to a call chain by using something called the kestrel pattern.

jwvh
  • 50,871
  • 7
  • 38
  • 64
  • I like your solution a lot because it allows me to send the accu result through an appended chain. For example: `def in = Stream.iterate(Document())(_.update(io.StdIn.readLine)); /* any other stuff in between */ for(doc <- in) { println(doc) }`. I can have object manipulation and object output at two separate parts of my code. I am missing only one thing (probably you have a cool solution for that too): I would like to *preprocess* the read lines in a same manner (separate block of code), without polluting my update callback. – ideaboxer Mar 26 '18 at 17:55
  • Some possibilities come to mind, but I'm not sure I understand the question. For example: you say you want to process the `readLine` text. Does that mean the document is updated with the processed result, or is the processing a side effect and the update is done with the unprocessed text? Sounds like this is worthy of a fresh question where it will get more eyes and attention than it will by updating the current question. – jwvh Mar 26 '18 at 19:15
  • Probably it is worth a fresh question. But I would like to state my objective more concisely: You provided me with a solution which allows me postprocessing the output of my document update. Now I am seeking a similar solution for the input. Coming back to your question: It means the document is updated with the processed result. The result should be a chain of "mapping" functions which acts as follows: `line -> (a) prepare(line) -> (b) use the line to update the document -> (c) do something with the resulting document`. – ideaboxer Mar 26 '18 at 19:53
  • Of course it should also be possbile to do `line -> (a) -> (a) -> (b) -> (c) -> (c) -> (c)` (as many chain links as needed). Concrete example: `line -> (a) trim(line) -> (a) filter comment lines -> (b) update the document -> (c) -> save the resulting document object to the undo history -> (c) print any errors -> (c) print statistics`. I would like to be able to flexibly plug together as many chain links as needed. Hence I need the update callback to only do the update, but nothing else (like reading a line from standard input, which should always happen at the beginning of the chain). – ideaboxer Mar 26 '18 at 20:03
  • 1
    Answer updated. Asking a new, clearer, question should still be considered. – jwvh Mar 27 '18 at 00:09