Reputation: 1250
I am just trying to figure out how immutable things like a List are working, and how I can add things to it?
I am very sorry for asking such dumb questions, but why is here my list always empty when printing it out?
var end = false
val list = List()
while (!end) {
val input = scala.io.StdIn.readLine("input:")
if (input == "stop" ) end = true
else input :: list
}
println(list)
}
Sorry for my inconvenience and this rather stupid question!
Upvotes: 1
Views: 730
Reputation: 1250
Thank you for all your help, I appreciate your help so much from all of you! I should have taken a closer look at recursion since it seems to be really important like in Scala! But trough your help I am getting an better idea of how it works!
I just tried to figure out how your solutions are working and created my own:
val list = List()
def scanInput(acc: List[String]): List[String] = {
val input = scala.io.StdIn.readLine("input:")
input match {
case "stop" => acc
case _ => scanInput(input :: acc)
}
}
println(scanInput(list))
Upvotes: 0
Reputation: 4499
In scala List is immutable.
When you add an item to list a new List
instance is crated with a item as its head and its tail now contains the previous list.
If you have list of "1,2,3" called intList
internally it is represented as
List(3, List(2, List(1, Nil) ) )
If you add an element 4
to this intList
List(4, intList )
Lets call this newList
Note intList
still contains List(3, List(2, List(1, Nil) ) )
.
If you want the intList
to refer the newList
You will have to do
intList = intList.add(4)
Change list from val
to var
. Then you can assign resulting List to list
variable
list = input :: list
Source: Online course on Scala called Functional Programming Principles in Scala
Upvotes: 1
Reputation: 369604
I am just trying to figure out how immutable things like a List are working, and how I can add things to it?
You can't. That's what immutable means, after all. If Latin is not your cup of tea, the English translation of immutable is unchangeable. It should be clear now, why you can't change something that is unchangeable.
I am very sorry for asking such dumb questions, but why is here my list always empty when printing it out?
You create an empty list, and you never change it (because it cannot be changed anyway). So, of course it is empty.
What can you can do, however, is create a new list which is almost exactly like the old list, except with a new item prepended to the front. That's what you are doing here:
input :: list
However, you don't assign this new list anywhere, you don't return it, you completely ignore it.
If you want to actually use your list in any way, you need to remember it somehow. The most obvious solution would be to assign it to a variable:
var end = false
var list: List[String] = List() // note: `var` instead of `val`
while (!end) {
val input = scala.io.StdIn.readLine("input:")
if (input == "stop" ) end = true
else list = input :: list // note: assign to `list`
}
println(list)
However, that's not very idiomatic. After all, we have now taken an immutable list and assigned it to a mutable variable … IOW, we have just moved the mutability around.
Instead, we could use a recursive solution:
def buildListFromInput(list: List[String] = List()): List[String] = {
val input = scala.io.StdIn.readLine("input:")
if (input == "stop") list else buildListFromInput(input :: list)
}
println(buildListFromInput())
This solution is not only recursive, the recursive call is also in tail position (IOW, the method is tail-recursive), which means that it will be just as efficient as a while
loop (in fact, it will be compiled into a while
loop, or more precisely, into a GOTO
). The Scala Language Specification guarantees that all implementations of Scala must eliminate direct tail-recursion.
Upvotes: 7
Reputation: 2069
Try rewriting the code in more functional way. Every operation on Immutable data structures return new instance with change. So ::
operator creates new List with input
on front. You might want to try rewrite this code as tail recursive function as follows.
@tailrec
def scanInput(continue: Boolean,acc: List[String]): List[String] = {
val input = scala.io.StdIn.readLine("input:")
if(!continue) acc
else scanInput(input != "stop", input :: acc)
}
Above code has no mutating state and it suits more Scala functional style.
Upvotes: 1
Reputation: 261
The reason
println(list)
is only printing out an empty list is because the bit
input :: list
isn't actually mutating the list itself. It is simply, in this case, very temporarily, creating a list containing the input at the front.
Try
println(input :: list)
or
val newList = input :: list
println(newList)
and you'll see what I mean.
Upvotes: 2