bajro
bajro

Reputation: 1250

Immutable Lists in Scala

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

Answers (5)

bajro
bajro

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

shanmuga
shanmuga

Reputation: 4499

In scala List is immutable.

Then how can I add items to the list?

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)


How can I fix my code

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

Jörg W Mittag
Jörg W Mittag

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

Haito
Haito

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

turbo_laser
turbo_laser

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

Related Questions