Лучший функциональный подход - PullRequest
3 голосов
/ 13 мая 2010

У меня есть изменчивый код scala, который я пытаюсь переписать в более функциональном стиле. Это довольно сложный кусок кода, поэтому я пытаюсь реорганизовать его по частям. Моя первая мысль была такая:

def iterate(count:Int,d:MyComplexType) = {
  //Generate next value n
  //Process n causing some side effects
  return iterate(count - 1, n)
}

iterate(2000000,initialValue)

Мне это не показалось функциональным, поскольку в моем коде все еще есть побочные эффекты. Моя вторая мысль была такой:

def generateStream(d:MyComplexType):Stream[MyComplexType] = {
  //Generate next value n
  return Stream.cons(n, generateStream(n))
}

for (n <- generateStream(initialValue).take(2000000)) {
  //process n causing some side effects
}

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

Это оставляет мне 3 варианта:

  1. Напишите хвостовую рекурсивную функцию, откусите пулю и выполните рефакторинг кода обработки значения
  2. Используйте ленивый список. Это не чувствительное к памяти приложение (хотя оно чувствительно к производительности)
  3. Придумайте новый подход.

Полагаю, я действительно хочу получить лениво оцененную последовательность, в которой я могу отбросить значения после их обработки. Есть предложения?

Ответы [ 2 ]

6 голосов
/ 13 мая 2010

Помните, что критичные к производительности алгоритмы часто работают лучше, когда они изменчивы. Так что остерегайтесь преждевременной де-оптимизации!

В Scala 2.8 вы можете использовать Iterator.iterate для создания бесконечной последовательности без сохраненных значений. Но я не уверен, что один будет ключевым шагом к рефакторингу кода, чтобы сделать его более функциональным. Это та часть "обработки данных с побочными эффектами", которая хитрая.

Вы можете поместить все это в блок итерации:

Iterator.iterate(initialState)(x => {
  // create a new state based upon state x
}).drop(2000000).next

, где вы теперь определили бесконечный поток обработки, последовательно изменяя ваше начальное состояние. Вы отбрасываете первые 2000000 значений (одно из которых является начальным значением), а затем получаете следующее (которое является 2000000-м сгенерированным значением). Попробуйте это с 0 и x => x + 1, чтобы увидеть это в действии.

3 голосов
/ 13 мая 2010

Я думаю, вы хотите использовать Range, если все, что вы делаете, - это создание Seq для целых чисел от 0 до 2000000. Это приятно и лениво, по крайней мере, в 2.7.

...