Scala confused about mutability and Array/Lists
Given a simple Matrix implementation
class Matrix(val matrix: Array[Array[Double]]) {
...
开发者_开发知识库 def add(scalar:Double) { matrix.map(_.map( _ + scalar )) }
def set(row:Int,col:Int,value:Double) { matrix(row)(col) = value }
...
}
To my understanding Mutability means I can't reassign matrix but I can set new values within it. As demonstrated by set() working.
The implementation of add right now is broken, but i'm stuck as to how to best implement it. I was hopeful about using transform
def add(scalar:Double) { matrix.transform(_.transform( _ + scalar )) }
// type mismatch; found : ...WrappedArray[Double] required: Array[Double]
But I can't get it to compile yet. So is there a way to get above example to work?
And please feel free to hammer any flaws in my logic about list/array mutability :P
Indeed, you can't reassign to matrix
, because it is a val
(the reference is immutable). Instances of Array
can be changed, because the interface of Array
so allows, which makes it a mutable class.
So matrix =
is not allowed. The call with matrix.map
is valid, it creates a new array, leaving matrix
untouched. It also changes the sub arrays inside matrix
, because of the second map
. Not what you want. Finally, the result is not assignable to matrix
, because assignments to matrix
are not allowed
transform
is indeed the way to go. But the arrays you want to make changes in are the ones in matrix
(the lines), not matrix
itself, which should still contain the same (but changed) lines. The proper call is
matrix.foreach(_.transform(_ + scalar)
Yet, your version should have worked. transform
returns the target of the call. The point is that you can chain calls, a.transform(...).doSomethingElse().andAgain()
(which you do not need here). So your matrix.transform should have been a transform(identity) - with the side effect that this particular identity would change the content of the lines, and that would have been fine.
The problem is that transform is not really a method of Array (arrays are in the JVM and have no such method). It comes from an implicit conversion. There are two notable implicit conversions from Array, one to ArrayOps
, another one to WrappedArray
(details in the article Fighting Bit Rot with Types, towards the end). Method transform
is in WrappedArray
. Its return type has to be WrappedArray
, because transform is defined well above in the type hierarchy, and forces the result to be this
. Which means the not too interesting result of transform
should not be used for arrays. So you must go with foreach
.
You need to do something with the return value of the matrix.map(_.map( _ + scalar ))
. Create a new matrix based on this value and return it:
def add(scalar:Double) = { new Matrix(matrix.map(_.map( _ + scalar ))) }
You can't assign to matrix
again, since this is a property of val
(unability to assign a new value).
The usage is then:
val matrix = new Matrix(Array(Array(1,2)))
val newMatrix = matrix.add(5)
精彩评论