Radfahrer
Radfahrer

Reputation: 121

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 empty list ytmp and modify it later like I am used to i C++. Thanks for the help

Upvotes: 1

Views: 1671

Answers (6)

Radfahrer
Radfahrer

Reputation: 121

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 ?

Upvotes: 0

Channing Walton
Channing Walton

Reputation: 4007

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)))

Upvotes: 0

Rex Kerr
Rex Kerr

Reputation: 167891

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.

Upvotes: 5

Channing Walton
Channing Walton

Reputation: 4007

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

Upvotes: 1

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297185

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.

Upvotes: 1

Monkey
Monkey

Reputation: 1

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.

Upvotes: 0

Related Questions