This question already has an answer here:
Question about Scala variable Mutability
(1 answer)
Closed 10 years ago.
Consider the following:
scala> val myset = Set(1,2)
myset: scala.collection.immutable.Set[Int] = Set(1, 2)
scala> myset += 3
<console>:9: error: reassignment to val
myset += 3
^
scala> var myset = Set(1,2)
myset: scala.collection.immutable.Set[Int] = Set(1, 2)
scala> myset += 3
scala> myset
res47: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
In one case I can add "alex" and in another I can't.
I know the difference between val and var. However what confused me in both cases Scala tells me that the Set is immutable but it allows different behaviour.
I don't want to just as that's because in oncase myset is a val and in one it is a var. I want a deeper answer than that to explain why in both cases Scala says myset is an immutable set but yet treats both differently. Because it is counter intuitive.
For example, is there any difference using a mutuable set and declaring an immutable set as var?
And why does scala let you bend the rules? Would it not be better if when it is said immutable it meant it?
Thanks.
First of all let's translate the += call
val myset = Set(1,2)
myset = myset + 3 //this is what the compiler does for myset += 3
This means that we're actually calling the + method on Set, whose scaladoc says
Creates a new set with an additional element, unless the element is already present.
What the code is trying to do is therefore to create a new Set with the added element and reassign it to the immutable reference myset.
Now if we change the reference to a mutable one (using var) then you can reassign it with the newly made and immutable Set(1,2,3)
The original Set(1, 2) is still immutable and no rule is broken. Let's explain this with a case
var myset = Set(1,2)
val holdIt = Some(myset)
myset += 3
println(myset) // will print Set(1, 2, 3)
println(holdIt)// will print Some(Set(1, 2))
As you can see, the original Set, which is captured by the Option, was never changed, we just reassigned it's variable reference to a newly created Set
When you use an immutable Set, a new Set will be created and reassigned to the var, which is not possible when using val. But a mutable Set can be mutated. While the following is not a perfectly save demonstration, it still shows that with an immutable Set a new object is created, and the mutable Set instance stays the same:
scala> var set1 = Set(1, 2)
set1: scala.collection.immutable.Set[Int] = Set(1, 2)
scala> System.identityHashCode(set1)
res0: Int = 2119337193
scala> set1 += 3
scala> System.identityHashCode(set1)
res2: Int = 1866831260
scala> var set2 = collection.mutable.Set(1, 2)
set2: scala.collection.mutable.Set[Int] = Set(2, 1)
scala> System.identityHashCode(set2)
res3: Int = 594308521
scala> set2 += 3
res4: scala.collection.mutable.Set[Int] = Set(2, 1, 3)
scala> System.identityHashCode(set2)
res5: Int = 594308521
Consider myset as the pointer and Set(1,2) as the object
In case of val myset pointer is assigned only once & cannot point to different object (i.e. pointer is immutable) where as
in case of var myset can be assigned arbitrary times & can point to different objects (i.e. pointer is mutable)
In both cases object Set(1,2) is immutable.
When you do
scala> val myset = Set(1,2)
myset: scala.collection.immutable.Set[Int] = Set(1, 2)
scala> myset += 3
<console>:9: error: reassignment to val
myset += 3
^
myset += 3 actually gives you a new Set with 3 appended to it..
Since the myset pointer is immutable you cannot reassign to the new object returned by the + method.
where as in case of
scala> var myset = Set(1,2)
myset: scala.collection.immutable.Set[Int] = Set(1, 2)
scala> myset += 3
Since myset pointer is mutable you can reassign to the new object returned by the + method.
Related
I'm learning Scala, and got confused about Seq.head.
scala> x = Array(1, 2, 3)
x: Array[Int] = [I#545e9f15
scala> x
res64: Array[Int] = Array(1, 2, 3)
scala> x(0)
res65: Int = 1
scala> x.head
res66: Int = 1
scala> x(0) += 1
scala> x.head += 1
<console>:13: error: value += is not a member of Int
x.head += 1
^
scala> x.head = 1
<console>:12: error: value head_= is not a member of scala.collection.mutable.ArrayOps[Int]
x.head = 1
^
scala>
Seems there's some implicit conventions happening beneath.
But from Scala API, the type of Array.head is Int (in my case):
def head: T
So why can I modify this element?
The key difference in your question is between x(0) += 1 (which works), and x.head += 1 (which fails).
x(0) += 1 is equivalent to x(0) = x(0) + 1, which is syntaxic sugar to x.update(0, x.apply(0) + 1). This instruction increases the value of x(0).
x.head returns and Int, which is immutable, thus x.head += 1 fails.
I believe this excerpt from the documentation might be useful:
val numbers = Array(1, 2, 3, 4)
val first = numbers(0)
numbers(3) = 100
Arrays make use of two common pieces of Scala syntactic sugar, shown
on lines 2 and 3 of the above example code. Line 2 is translated into
a call to apply(Int), while line 3 is translated into a call to
update(Int, T).
When you use parenthesis on the left side of assignments they are converted to an update call that replaces the element in the array. The .head method does not have such conversion, it is merely a way to get the first element so you can not use it to modify the list.
Does val or var make difference in immutable objects like lists or tuple?
scala> val ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
scala> var ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
I am beginner in scala.
You may be confusing two different aspects of immutability. The local variable ab refers to some object in memory, in this case a List. When you declare ab as a val you are instructing the compiler that ab will always refer to the same object. Because List is immutable, its contents will never change, but a var referring to it might be reassigned to some other List.
scala> import scala.collection.mutable.MutableList
import scala.collection.mutable.MutableList
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
scala> val b = MutableList(1,2,3,4)
b: scala.collection.mutable.MutableList[Int] = MutableList(1, 2, 3, 4)
scala> var c = List(1,2,3,4)
c: List[Int] = List(1, 2, 3, 4)
Here, a is a val containing an immutable data structure. a refers to List(1,2,3,4) and will always do so.
The val b refers to a MutableList. We can change the internal contents of the MutableList but we cannot assign b to a different MutableList.
scala> b += 5
res6: b.type = MutableList(1, 2, 3, 4, 5)
scala> b = MutableList(2,3,5,7)
<console>:12: error: reassignment to val
b = MutableList(2,3,5,7)
^
With var c we have a variable that refers to an immutable data structure List but that can be reassigned to a different List.
scala> c = c :+ 5
c: List[Int] = List(1, 2, 3, 4, 5)
Note that the :+ operator (unlike the += operator above) does not change the List referred to by c. Instead it create a copy of the List with the element 5 appended. Because c was declared a var we can then assign this new list to c.
It's not really relevant whether the object that ab points to is mutable. val means that you cannot in the future assign ab to another value, while var allows it.
Try repeating the assignment in each case and see what happens:
scala> val ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
scala> ab = List(1,2,3)
reassignment to val; not found: value ab
scala> var ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
scala> ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
It's a question of style.
In Scala using a val is generally preferred to using a var as in (functional) programming immutability makes it easier to reason about a program.
So if you can get what you want without resorting to var it is the way to go.
A typical application of a var would be if you want to use an immutable data structure and update it benifitting of structural sharing.
var data = List.empty[String] // var is importan here
def addToData(s: String) : Unit = { data = s :: data }
The same could be achieved by using a mutable datastructure
import scala.collection.mutable.ArrayBuffer
val data = ArrayBuffer.empty[String]
data += "Hello" // this function is here from the beginning
For an in depth discussion look at https://stackoverflow.com/a/4440614/344116 .
I need to create a new instance of BitSet class from another BitSet object (input).
I expected something like new BitSet(input), but none found. I could get the new instance with map() method as follows, but I don't think this is the best solution.
var r = input.map(_ + 0)(BitSet.canBuildFrom)
What's the copy constructor of BitSet? What's the general rule for copy constructor in Scala?
You can create another with the bitmask of the first:
var r = new BitSet(input.toBitMask)
I think, the general rule is to use immutable collections. They are, well, immutable, so you can pass them around freely without taking special care for copying them.
When you need mutable collections, however, copying collections becomes useful. I discovered that using standard to method works:
scala> mutable.Set(1, 2, 3)
res0: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> res0.to[mutable.Set]
res1: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> res0 eq res1
res2: Boolean = false
However, it won't work with BitSet because it is not a generic collection, and to needs type constructor as its generic parameter. For BitSet you can use the method suggested by Lee. BTW, it is intended exactly for scala.collection.mutable.BitSet, because scala.collection.immutable.BitSet does not contain such constructor (nor does it need it).
The "copy" method on collections is called clone (to be consistent with Java style).
scala> collection.mutable.BitSet(1,2,3)
res0: scala.collection.mutable.BitSet = BitSet(1, 2, 3)
scala> res0.clone
res1: scala.collection.mutable.BitSet = BitSet(1, 2, 3)
scala> res0 += 4
res2: res0.type = BitSet(1, 2, 3, 4)
scala> res1
res40: scala.collection.mutable.BitSet = BitSet(1, 2, 3)
In Scala: There appears to be no remove() method for LinkedList, for some reason I can't understand. There is one for DoubleLinkedList though, but I don;t need a double linked list.
Also, how could I set the current element to the first? And how do I know when it has reached the end (is it null?)
There is no current element in LinkedList.
There are only 2 fields in LinkedList: elem (or head) for element and next (or tail) for LinkedList.
You can test if LinkedList is empty using isEmpty. Every LinkedList contains an empty LinkedList as it's deepest tail. See documentation for ASCII illustration.
You can delete Nth element (if any) like this:
var current = myLinkedList.drop(n-2)
current.next = current.next.next
Example:
scala> val myList = LinkedList(1,2,3,4,5)
myList: scala.collection.mutable.LinkedList[Int] = LinkedList(1, 2, 3, 4, 5)
scala> val current = myList.drop(1)
current: scala.collection.mutable.LinkedList[Int] = LinkedList(2, 3, 4, 5)
scala> current.next = current.next.next
current.next: scala.collection.mutable.LinkedList[Int] = LinkedList(4, 5)
scala> myList
res0: scala.collection.mutable.LinkedList[Int] = LinkedList(1, 2, 4, 5)
You can't delete first element this way, but you can just forget about it:
scala> var myList = LinkedList(1,2,3,4,5)
myList: scala.collection.mutable.LinkedList[Int] = LinkedList(1, 2, 3, 4, 5)
scala> myList = myList.next
myList: scala.collection.mutable.LinkedList[Int] = LinkedList(2, 3, 4, 5)
first of all, is there a particular reason why you need a LinkedList over the standard List in Scala?
As warned in the scaladoc, you could easily end up in NPEs with LinkedList, whereas a List won't have this issue unless you actually introduce null values.
a List tail method is usually all you need. Please consider this approach first, and only resort to a specialized class if absolutely necessary.
I wanted to add items dynamically into an array. But it seems Scala arrays and lists don't provide any methods for adding items dynamically due to the immutable nature.
So I decided to use List data type to make use of this :: method to achieve this. My code look like this
var outList = List(Nil)
val strArray = Array("ram","sam","bam")
for (str<-strArray)
outList = str :: outList
Though it works in some way, the problem is the new strings are pre-appended into the list. But the ideal requirement is order of the data. Yeah, I know what you are thinking, you can reverse the final result list to get the original order. But the problem is it's a huge array. And I believe it's not a solution though it solves the problem. I believe there should be a simple way to solve this...
And my reason for hacking Scala is to learn the functional way of coding. Having var (mutable type) and populating the list on the fly seems to me is not a functional way of solving things.
How can I do it?
Ideally, I want to achieve something like this in Scala (below the C# code)
List<int> ls = new List<int>();
for (int i = 0; i < 100; i++)
ls.Add(i);
But it seems Scala Arrays & Lists doesn't provide any methods for adding items dynamically due to the immutable nature.
Well, no. Scala Arrays are just Java arrays, so they are mutable:
val arr = Array(1,2)
arr(0) = 3 // arr == Array(3, 2)
But just as Java (and C/C++/C#/etc.) arrays, you can't change the size of an array.
So you need another collection, which is backed by an array, but does allow resizing. A suitable collection in Scala is scala.collection.mutable.ArrayBuffer, java.util.ArrayList in Java, etc.
If you want to get a List instead of an Array in the end, use scala.collection.mutable.ListBuffer instead.
Going after retronym's answer:
If you still want to use list there are a few ways to prepend an item to the list. What you can do is (yes the top part is still wrong):
scala> var outList : List[String] = Nil
outList: List[String] = List()
scala> val strArray = Array("a","b","c")
strArray: Array[java.lang.String] = Array(a, b, c)
scala> for(s <- strArray)
| outList = outList :+ s
scala> outList
res2: List[String] = List(a, b, c)
Note the :+ operator. If you rather append, you'd use s +: outList.
Now who says programming in Scala isn't fun? ;)
P.S. Maybe the reason why you'd want to make them immutable is the speed. Handling large data will be more efficient with immutable data types. Am I right?
If you want to use a mutable Buffer, as retronym mentioned. It looks like this:
scala> var outList = scala.collection.mutable.Buffer[String]()
outList: scala.collection.mutable.Buffer[String] = ArrayBuffer()
scala> for(str<-strArray) outList += str
scala> outList
res10: scala.collection.mutable.ListBuffer[String] = ListBuffer(ram, sam, bam)
Anyway, perhaps it is better to directly do the things you want to do with the strArray. E.g:
strArray map(_.toUpperCase) foreach(println)
If you want to work with immutable structures, you can use the ++ method:
scala> val orgList = List(1,2,3)
orgList: List[Int] = List(1, 2, 3)
scala> val list2Add = List(4,5,6)
list2Add: List[Int] = List(4, 5, 6)
scala> val newList = orgList ++ list2Add
newList: List[Int] = List(1, 2, 3, 4, 5, 6)
If you want to do more work on the elements than just adding them you can use higher order functions:
val newList = orgList ++ list2Add.map(_ * 2)
newList: List[Int] = List(1, 2, 3, 8, 10, 12)
Or with a for loop:
val newList = orgList ++ {for(x <- list2Add) yield 2*x}
Or you could create some recursive loop:
def addAll(toList: List[Int], fromList: List[Int]): List[Int] =
fromList match {
case x :: tail => addAll(2*x :: toList, tail)
case Nil => toList
}
val newList = addAll(orgList, list2Add )
but in this case the ordering of the added elements will be in reversed:
List(12, 10, 8, 1, 2, 3)
If you want performance when working with lists, it is better to reverse the result than try to add new elements in the end. Adding elements in the end to a list is nooot good :-)
Okay, there are a few things to clear up.
This is wrong, you're make a one element list, containing an empty list:
scala> var outList = List(Nil)
outList: List[object Nil] = List(List())
Nil is the empty list:
scala> var outList: List[String] = Nil
outList: List[String] = List()
Or, if you prefer:
scala> var outList = List[String]()
outList: List[String] = List()
Without more context, it's hard to know what you mean by 'dynamically'. Your example code would be better written as:
scala> val strArray = Array("ram","sam","bam")
strArray: Array[java.lang.String] = Array(ram, sam, bam)
scala> strArray toList
res0: List[java.lang.String] = List(ram, sam, bam)
If you want a mutable collection that can grow and efficiently handle prepend, append, and insert operations, you could use scala.mutable.Buffer.
If you are looking to create a new collection, you can use the yield key word:
val outlist = for(i <- 0 to 100) yield i
Or:
val arrList = "Some" :: "Input" :: "List" :: Nil
val outlist = for ( i <- arrList ) yield i
Technically, outlist is a Seq in both of the above examples so you may need to call the toList method on it if you need some of List's methods.
We can use ArrayBuffer as suggested.
However, i have created a simple program which takes no of parameters for an Array of int and it will let you enter the numbers and finally we are displaying them.
val arraySize = scala.io.StdIn.readLine().toInt
val arr = new ArrayBuffer[Int]() ++ (1 to arraySize).map{
i =>
scala.io.StdIn.readLine().toInt
}
println(arr.mkString(","))