Scala функциональное программирование с векторами - PullRequest
1 голос
/ 17 декабря 2010

Я новичок в программировании Scala и испытываю некоторые трудности с функциональным программированием и неизменными коллекциями. Я пытаюсь перенести мой числовой код из C ++ в Scala.

Очень часто у меня есть код, работающий с маленьким вектором, например:

double ytmp[6];

for (int i=0; i<6; i++) {
    ytmp[i] = y[i] + h*(a41*dydx[i] + a42*k2[i] + a43*k3[i]);
}

Как мне написать что-то подобное в эффективном Scala? Вначале я думал об использовании простых списков, но у меня были проблемы с неизменяемыми типами, поэтому я не могу просто создать пустой список ytmp и изменить его позже, как я привык к C ++. Спасибо за помощь

Ответы [ 6 ]

5 голосов
/ 17 декабря 2010

Для эффективности вы пишете это точно так же (заметьте, это , а не функциональный код ), за исключением цикла while. Циклы for преобразуются в невероятно неэффективные (но все же великолепные и мощные) конструкции. Итак, в этом случае вы (предупреждение, не проверено):

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
}

, который обычно будет работать так же быстро, как и ваш код на C ++.

Вы начинаете получать выгоду от Scala, как только вы оборачиваете такие примитивы в классы, а затем оперируете большим количеством этих классов с помощью map, foreach и т. Д.

Но для числового кода вы либо пишете красивый, но смущающе медленный код (с точки зрения C ++), либо некрасивый, но прилично производительный код (да, некрасиво по сравнению с C-подобным C ++).

Так как я часто этим занимаюсь, я стараюсь поместить этот уродливый, но быстрый код в красивые библиотеки, а затем как можно больше забыть об этом и работать на более высоком уровне, поддерживаемом библиотеками.

1 голос
/ 17 декабря 2010

Вот еще одна возможность - не знаю, является ли ее исполнитель и т. Д .:

ytmp.zipWithIndex map ((yi) => yi._1 + h*(a41*dydx(yi._2) + a42*k2(yi._2) + a43*k(yi._2)))

, которая возвращает массив

1 голос
/ 17 декабря 2010

Вот как я бы это написал:

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

Итак, это первое преобразование, которое, за исключением любых ошибок с моей стороны, должно делать то же самое.Есть и другие соображения, такие как использование неизменяемой коллекции вместо Array или инициализация массива во время создания.Однако массивы будут быстрее для того типа кода, который вы пишете.Один из интересных вариантов инициализации массива был бы следующим:

def func(i: Int) = y(i) + h * (a41 * dydx(i) + a42 * k2(i) + a43 * k3(i))
val ytmp = Array.tabulate[Double](6)(func)

Однако я не уверен, что tabulate даст вам такую ​​же производительность.Может быть.

0 голосов
/ 18 декабря 2010

Пух, есть много разных решений этой проблемы, большое спасибо. Специально для эталонных номеров. Поэтому я думаю, что буду придерживаться решения «.view.zipWithIndex» и надеюсь, что скорость будет достаточно хорошей, в противном случае мне придется вернуться к циклам while.

Еще одна короткая проблема, с которой я сталкиваюсь. Мне нужно что-то вроде этого:

List results
while(step<nsteps) {
    so something
    if successful
        results.append(result)
}

Если я правильно понимаю, я всегда должен готовиться к списку. Вот как бы я это сделал:

var list: List[Double] =  Nil
while(step<nsteps) {
    var result = 4.0
    list =  result :: list
}
list = list.reverse

Это хорошая идея или есть лучшее решение для производительности?

0 голосов
/ 18 декабря 2010

Вот еще один лайнер, но я подозреваю, что производительность будет плохой:

Array(ytmp, dydx, k2, k3).transpose.map(l => l(0) + h*(a41*l(1) + a42*l(2) + a43*l(3)))
0 голосов
/ 17 декабря 2010

Вы можете попробовать что-то вроде

val v = for(i <- 1 to 10) yield y(i) + h*(a41*dydx(i) + a42*k2(i) + a43*k(i))

Значение v будет IndexedSeq, реализованным с использованием Vector, который является неизменяемым типом, созданным для индексированных поисков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...