How to catch empty lines in user input for Scala? - scala

Consider the following code
val sc = new java.util.Scanner (System.in)
while (sc.hasNext()) {
var temp = sc.nextLine()
println(temp.nonEmpty)
println (temp)
}
whenever I press enter with empty line the program simply waits for next input to happen and does not execute the statement inside
For example
input:
<enter>
<enter>
1
cause
output:
<space>
false
<space>
false
1
true
I am trying to make user enter a list of lines, and exit on empty lines
eg.
1 2 3 4
5 6 7 8
<enter> // -> will exit
How would I achieve that?

I think this is a bit more idiomatic:
Iterator.continually(scala.io.StdIn.readLine).takeWhile(_.nonEmpty).foreach(line =>
println(s"read $line")
)

Add if (temp.isEmpty) sys.exit(1)

Related

Swift Command Line Tool - Read multiple lines

I know the readLine() method, but if copy paste a text with more than one lines, only the first line will be retrieved.
I would like to retrieve all the text that the user copy pastes.
Is it possible ?
You can call readLine() in a loop and exit the loop in a predefined way
var input: [String] = []
print("Enter text, finish by entering Q")
while let line = readLine(strippingNewline: true) {
if line.lowercased() == "q" { break }
input.append(line)
}
print(input)
Example
Enter text, finish by entering Q
a
b
c
q
["a", "b", "c"]
Program ended with exit code: 0

String interpolation of variable value

I want the variable value to be processed by string interpolation.
val temp = "1 to 10 by 2"
println(s"$temp")
output expected:
inexact Range 1 to 10 by 2
but getting
1 to 10 by 2
is there any way to get this way done?
EDIT
The normal case for using StringContext is:
$> s"${1 to 10 by 2}"
inexact Range 1 to 10 by 2
This return the Range from 1 to 10 with the step value of 2.
And String context won't work on variable, so can there be a way I can do like
$> val temp = "1 to 10 by 2"
$> s"${$temp}" //hypothetical
such that the interpreter will evaluate this as
s"${$temp}" => s"${1 to 10 by 2}" => Range from 1 to 10 by step of 2 = {1,3,5,7,9}
By setting a string value to temp you are doing just that - creating a flat String. If you want this to be actual code, then you need to drop the quotes:
val temp = 1 to 10 by 2
Then you can print the results:
println(s"$temp")
This will print the following output string:
inexact Range 1 to 10 by 2
This is the toString(...) output of a variable representing a Range. If you want to print the actual results of the 1 to 10 by 2 computation, you need to do something like this:
val resultsAsString = temp.mkString(",")
println(resultsAsString)
> 1,3,5,7,9
or even this (watch out: here the curly brackets { } are used not for string interpolation but simply as normal string characters):
println(s"{$resultsAsString}")
> {1,3,5,7,9}
Edit
If what you want is to actually interpret/compile Scala code on the fly (not recommended though - for security reasons, among others), then you may be interested in this:
https://ammonite.io/ - Ammonite, Scala scripting
In any case, to interpret your code from a String, you may try using this:
https://docs.scala-lang.org/overviews/repl/embedding.html
See these lines:
val scripter = new ScriptEngineManager().getEngineByName("scala")
scripter.eval("""println("hello, world")""")

Scala: scanLeft one item behind when reading from stdin

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.
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
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.

How to enter a multi-line command in the Scala REPL?

I would like to enter something like the following match instruction, but formatted across multiple lines. Is that possible in the Scala REPL?
myString match { case patt(a) => true case _ => false }
If you're just typing it in as-is, the REPL should detect the opening brace when you return, so that it won't try to parse and execute the code until it finds the closing brace.
You could also use paste mode by typing :pa or :paste. This will allow you to enter as much as you want in any format (two blank lines will automatically quit from it). Then when finished entering in code, you can press Ctrl + D to evaluate.
One way to enter into multi-line mode in the Scala REPL is to hit enter right after the opening curly brace "{", then hit enter after each line until the last closing curly brace has been entered "}". Hitting enter after it will exit the multi-line mode
myScript match { <enter> //enter multi-line mode
| case scriptStart(a) => true <enter>
| case _ => false <enter>
|} <enter> //exit multi-line mode
When cascading transformations, it is as simple as ending each line with a dot. For example:
val wordcount = sc.
textFile("MYFILE.txt").
flatMap( x => x.split(" ") ).
map( w => (w,1) ).
reduceByKey( (a,b) => a+b )

The expression prints itself in unexpected order

When I print a log information like this:
val idList = getIdList
log info s"\n\n-------idList: ${idList foreach println}"
It shows me:
1
2
3
4
5
-------idList: ()
That makes sense because foreach returns Unit. But why does it print the list of id first? idList is already evaluated in the previous line (if that's the cause)!
And how to make it print it in expected order - after idList:?
This is because you're not evaluating the log string to read what you want, you evaluate it to:
\n\n -------idList: ()
However, the members of the list appear in the output stream as a side effect, due to the println call in the string interpolation.
EDIT: since clarification was requested by the OP, what happens is that the output comes from two sources:
${idList foreach println} evaluates to (), since println itself doesn't return anything.
However, you can see the elements printed out, because when the string interpolation is evaluated, println is being called. And println prints all the elements into the output stream.
In other words:
//line with log.info() reached, starts evaluating string before method call
1 //println from foreach
2 //println from foreach
3 //println from foreach
4 //println from foreach
5 //println from foreach
//string argument log.info() evaluated from interpolation
-------idList: () //log info prints the resultant string
To solve your problem, modify the expression in the interpolated string to actually return the correct string, e.g.:
log info s"\n\n-------idList: ${idList.mkString("\n")}"
Interpolation works in a following way:
evaluate all arguments
substitute their results into resulting string
println is a Unit function that prints to the standard output, you should use mkstring instead that returns a string
log info s"\n\n-------idList: ${idList.mkString("(", ", ", ")")}"
As pointed out by #TheTerribleSwiftTomato , you need to give an expression that returns a value and has no other side-effect. So simply do it like this:
val idList = getIdList
log info s"\n\n-------idList: ${idList mkString " "}"
For example, this works for me:
val idList = List(1, 2, 3, 4, 5)
println(s"\n\n-------idList: ${idList mkString " "}")
Output:
-------idList: 1 2 3 4 5