I have the following class:
class Matrix(val matrix: Array[Array[Int]]) {
// some other methods
override def toString: String = {
return matrix.map(_.mkString(" ")).mkString("\n")
}
}
I have declared class variable as val to prevent further modification in matrix.
object Main {
def main(args: Array[String]) {
val > = Array
val x: Array[Array[Int]] = >(
>(1, 2, 3),
>(4, 5, 6),
>(7, 8, 9))
val m1 = new Matrix(x)
println("m1 -->\n" + m1)
x(1)(1) = 101 // Need to prevent this type of modification.
println("m1 -->\n" + m1)
}
}
After doing x(1)(1) = 101 the output of the program is
m1 -->
1 2 3
4 101 6
7 8 9
But I want to prevent this modification and get the original matrix as
m1 -->
1 2 3
4 5 6
7 8 9
Instead of using Array, maybe you could use List instead, and it is immutable :
scala> val num:List[Int] = List(1,2,3)
num: List[Int] = List(1, 2, 3)
scala> num(1) = 3
<console>:13: error: value update is not a member of List[Int]
num(1) = 3
^
There's a difference between a mutable/immutable variable (e.g. var x and val x) and a mutable/immutable collection. Declaring one (the variable) doesn't effect the other (the collection).
The Scala Array is inherited from Java and, as such, is mutable. There are many fine immutable collections. Array isn't one of them.
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 have a function that takes a list of lists of integer, specifically Seq[Seq[Int]]. Then I produce this data from reading a text file and using split, and that produces a list of Array. That is not recognized by Scala, who raises a match error. But either IndexedSeq or Array alone are OK with a Seq[Int] function, apparently only the nested collection is an issue. How can I convert implicitly IndexedSeq[Array[Int]] to Seq[Seq[Int]], or how else could I do this other than using toList as demonstrated below? Iterable[Iterable[Int]] seems to be fine, for instance, but I can't use this.
scala> def g(x:Seq[Int]) = x.sum
g: (x: Seq[Int])Int
scala> g("1 2 3".split(" ").map(_.toInt))
res6: Int = 6
scala> def f(x:Seq[Seq[Int]]) = x.map(_.sum).sum
f: (x: Seq[Seq[Int]])Int
scala> f(List("1 2 3", "3 4 5").map(_.split(" ").map(_.toInt)))
<console>:9: error: type mismatch;
found : List[Array[Int]]
required: Seq[Seq[Int]]
f(List("1 2 3", "3 4 5").map(_.split(" ").map(_.toInt)))
^
scala> f(List("1 2 3", "3 4 5").map(_.split(" ").map(_.toInt).toList))
res8: Int = 18
The problem is that Array does not implement SeqLike. Normally, implicit conversions to ArrayOps or WrappedArray defined in scala.predef allow to use array just like Seq. However, in your case array is 'hidden' from implicit conversions as a generic argument. One solution would be to hint compiler that you can apply an implicit conversion to the generic argument like this:
def f[C <% Seq[Int]](x:Seq[C]) = x.map(_.sum).sum
This is similar to Paul's response above. The problem is that view bounds are deprecated in Scala 2.11 and using deprecated language features is not a good idea. Luckily, view bounds can be rewritten as context bounds as follows:
def f[C](x:Seq[C])(implicit conv: C => Seq[Int]) = x.map(_.sum).sum
Now, this assumes that there is an implicit conversion from C to Seq[Int], which is indeed present in predef.
How about this:
implicit def _convert(b:List[Array[Int]]):Seq[Seq[Int]]=b.map(_.toList)
Redefine f to be a bit more flexible.
Since Traversable is a parent of List, Seq, Array, etc., f will be compatible with these containers if it based on Traversable. Traversable has sum, flatten, and map, and that is all that's needed.
What is tricky about this is that
def f(y:Traversable[Traversable[Int]]):Int = y.flatten.sum
is finicky and doesn't work on a y of type List[Array[Int]] although it will work on Array[List[Int]]
To make it less finicky, some type view bounds will work.
Initially, I replaced your sum of sums with a flatten/sum operation.
def f[Y<%Traversable[K],K<%Traversable[Int]](y:Y):Int=y.flatten.sum
I found this also seems to work but I did not test as much:
def f[Y <% Traversable[K], K <% Traversable[Int]](y:Y):Int=y.map(_.sum).sum
This <% syntax says Y is viewable as Traversable[K] for some type K that is viewable as a Traversable of Int.
Define some different containers, including the one you need:
scala> val myListOfArray = List(Array(1,2,3),Array(3,4,5))
val myListOfArray = List(Array(1,2,3),Array(3,4,5))
myListOfArray: List[Array[Int]] = List(Array(1, 2, 3), Array(3, 4, 5))
scala> val myArrayOfList = Array(List(1,2,3),List(3,4,5))
val myArrayOfList = Array(List(1,2,3),List(3,4,5))
myArrayOfList: Array[List[Int]] = Array(List(1, 2, 3), List(3, 4, 5))
scala> val myListOfList = List(List(1,2,3),List(3,4,5))
val myListOfList = List(List(1,2,3),List(3,4,5))
myListOfList: List[List[Int]] = List(List(1, 2, 3), List(3, 4, 5))
scala> val myListOfRange = List(1 to 3, 3 to 5)
val myListOfRange = List(1 to 3, 3 to 5)
myListOfRange: List[scala.collection.immutable.Range.Inclusive] = List(Range(1, 2, 3), Range(3, 4, 5))
Test:
scala> f(myListOfArray)
f(myListOfArray)
res24: Int = 18
scala> f(myArrayOfList)
f(myArrayOfList)
res25: Int = 18
scala> f(myListOfList)
f(myListOfList)
res26: Int = 18
scala> f(myListOfRange)
f(myListOfRange)
res28: Int = 18
I am playing with Scala's streams and I'm not sure I catch the idea.
Let's consider following code
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head, fun(s.tail))
executing this
val f = fun(Stream.from(7))
f take 14 foreach println
results with
7 8 9 10 ... up to 20
Let's say I understand this.
Now, changing slightly code (adding 2 to head)
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head + 2, fun(s.tail))
results in
9 10 11 ... up to 22
Again I think I understand. Problems starts with next example (d
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head / 2, fun(s.tail))
3 4 4 5 5 6 6 7 7 8 8 9 9 10
This I do not get, please explain why it results this way?
Similar, subtracting also does not behave as I expect
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head - 2, fun(s.tail))
Output
5 6 7 8 9 10 ... up to 18
Given your "take": 7 8 9 10 ... up to 20,
what happens when you + 2 on each element?
what happens when you / 2 on each element (int arithmetic)?
what happens when you - 2 on each element?
Is it more intuitive if you think of it as mapping the Stream?
scala> val s1 = Stream.from(10)
s1: scala.collection.immutable.Stream[Int] = Stream(10, ?)
scala> val s2 = s1 map (_ * 2)
s2: scala.collection.immutable.Stream[Int] = Stream(20, ?)
scala> s2.take(5).toList
res0: List[Int] = List(20, 22, 24, 26, 28)
scala> val s3 = s1 map (_ / 2)
s3: scala.collection.immutable.Stream[Int] = Stream(5, ?)
scala> s3.take(5).toList
res1: List[Int] = List(5, 5, 6, 6, 7)
scala> val s4 = s1 map (_ - 2)
s4: scala.collection.immutable.Stream[Int] = Stream(8, ?)
scala> s4.take(5).toList
res2: List[Int] = List(8, 9, 10, 11, 12)
Ok, let's try and break it down...
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head, fun(s.tail))
is a function that takes a Stream and separates its head and tail, applies itself recursively on the tail, and then recombines the two results with the cons operator.
Since the head is not touched during this operation, the Stream is rebuilt element by element as it was before.
val f = fun(Stream.from(7))
f it's the same as Stream.from(7) [i.e. an infinite sequence of increasing integers starting from 7]
Printing f take 14 in fact shows that we have the first 14 numbers starting from 7 [i.e. 7,8,9,...,20]
What happens next is that, while rebuilding the stream with the cons, each element is modified in some way
def fun(s: Stream[Int]): Stream[Int] = Stream.cons(s.head + 2, fun(s.tail))
This adds 2 to the head before recombining it with the modified tail. The latter is modified in the same way, its first element being added to 2 and then recombined to its own tail, and so own.
If we assume again that s contains the number from 7 on, what happens looks like
fun(s) = cons(7 + 2, cons(8 + 2, cons(9 + 2, ... ad infinitum ... )))))
This is the same as adding 2 to each and every element of the stream s.
The code confirms that by printing "9 to 22", which is exactly "7 to 20" with 2 added to every element.
The others examples are analogous:
the stream with each element divided by 2 (and rounded to the floor
integer value, since the Stream is typed with Int values)
the stream where each element is decremented by 2
When I evaluate a for in Scala, I get an immutable IndexedSeq (a collection with array-like performance characteristics, such as efficient random access):
scala> val s = for (i <- 0 to 9) yield math.random + i
s: scala.collection.immutable.IndexedSeq[Double] = Vector(0.6127056766832756, 1.7137598183155291, ...
Does a for with a yield always return an IndexedSeq, or can it also return some other type of collection class (a LinearSeq, for example)? If it can also return something else, then what determines the return type, and how can I influence it?
I'm using Scala 2.8.0.RC3.
Thanks michael.kebe for your comment.
This explains how for is translated to operations with map, flatMap, filter and foreach. So my example:
val s = for (i <- 0 to 9) yield math.random + i
is translated to something like this (I'm not sure if it's translated to map or flatMap in this case):
val s = (0 to 9) map { math.random + _ }
The result type of operations like map on collections depends on the collection you call it on. The type of 0 to 9 is a Range.Inclusive:
scala> val d = 0 to 9
d: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
The result of the map operation on that is an IndexedSeq (because of the builder stuff inside the collections library).
So, to answer my question: the result of a for (...) yield ... depends on what type is inside the parantheses. If I want a List as the result, I could do this:
scala> val s = for (i <- List.range(0, 9)) yield math.random + i
s: List[Double] = List(0.05778968639862214, 1.6758775042995566, ...
You can always transform a range to a list using toList:
> val s = for (i <- (0 to 9).toList) yield math.random + i
> s : List[Double]