Scala functional programming with vectors
I am new to Scala programming and have some difficulty with functional programming and immutable collections. I am trying to port my numeric code from C++ to Scala.
A lot of time I have code operating on small vector like this:
double ytmp[6];
for (int i=0; i<6; i++) {
ytmp[i] = y[i] + h*(a41*dydx[i] + a42*k2[i] + a43*k3[i]);
}
How do I write something like this in efficient Scala? I thought about using simple lists in the beginning, but have problems with the immutable types, so I can't just create a empt开发者_Go百科y list ytmp and modify it later like I am used to i C++. Thanks for the help
For efficiency, you write this exactly the same way (note, this is not functional code), except with a while loop. For loops are converted into incredibly inefficient (yet splendidly general and powerful) constructs. So, in this case, you (warning, untested):
val ytmp = new Array[Double](6)
var i = 0
while (i < 6) {
ytmp(i) = y(i) + h*(a41*dydx(i) + a42*k2(i) + a43*k3(i))
i += 1
}
which will run about as fast as your C++ code, typically.
You start to benefit from Scala once you wrap primitive stuff like this into classes, and then operate on large numbers of those classes with map
, foreach
, and so on.
But for numeric code, either you write pretty but embarrassingly slow code (from a C++ perspective), or ugly but decently-performing code (yes, ugly compared to C-like C++).
Since I do this a lot, I try to put this ugly but fast code in nice libraries and then forget about it as much as possible and work at the higher level enabled by the libraries.
This is how I'd write it:
// C++
double ytmp[6];
// Scala
val ytmp = new Array[Double](6)
// C++ (elided the computation to make conversion clearer)
for (int i=0; i<6; i++) {
ytmp[i] = func(i);
}
// Scala
for (i <- ytmp.indices)
ytmp(i) = func(i)
// C++
double func(int i) {
return y[i] + h*(a41*dydx[i] + a42*k2[i] + a43*k3[i]);
}
// Scala
def func(i: Int) = y(i) + h * (a41 * dydx(i) + a42 * k2(i) + a43 * k3(i))
So, this is a first transformation, which, barring any mistakes on my part, should do the same thing. There are other considerations like using an immutable collection instead of Array
, or initializing the array at the time of creation. However, arrays will be faster for the kind of code you are writing. One interesting option for array initialization would be this:
def func(i: Int) = y(i) + h * (a41 * dydx(i) + a42 * k2(i) + a43 * k3(i))
val ytmp = Array.tabulate[Double](6)(func)
However, I'm not sure tabulate
will give you the same performance. Maybe.
Here's another possibility - no idea if its performant etc:
ytmp.zipWithIndex map ((yi) => yi._1 + h*(a41*dydx(yi._2) + a42*k2(yi._2) + a43*k(yi._2)))
which returns an array
You could try something like
val v = for(i <- 1 to 10) yield y(i) + h*(a41*dydx(i) + a42*k2(i) + a43*k(i))
The value v will be an IndexedSeq implemented using a Vector, which is an immutable type built for indexed lookups.
Here is another one liner but I suspect the performance will be bad:
Array(ytmp, dydx, k2, k3).transpose.map(l => l(0) + h*(a41*l(1) + a42*l(2) + a43*l(3)))
puh there are a lot of different solutions to this problem, thanks very much. Especially for the benchmark numbers. So I think I will stick with the ".view.zipWithIndex" solution and hope the speed is good enough, otherwise I have to go back to while loops.
Another short problem I face. I need something like this:
List results
while(step<nsteps) {
so something
if successful
results.append(result)
}
If i understand it right I should always prepend to a list. So this is how I would do it:
var list: List[Double] = Nil
while(step<nsteps) {
var result = 4.0
list = result :: list
}
list = list.reverse
Is this a good idea, or is there a better solution for the performance ?
精彩评论