2014-09-30 2 views
4

Недавно я начал кодировать множество своих соревнований по программированию в Scala. (Вы можете взглянуть на платформу здесь - http://codeforces.com/)Какую конструкцию для итерации лучше использовать в Scala?

Что касается природы проблем, мне часто приходится перебирать массив или входные данные. Например. есть заявление о проблеме, в котором говорилось, что на первой строке ввода я получу номер M, а затем мне нужно прочитать M строк или целые числа или что угодно. Я стараюсь использовать другой подход к этому:

for (i <- 0 until M) 
---- 
(0 until M).foreach 
---- 
var i = 0 
while (i < M) 
--- 

Или даже хвостовая рекурсия

@tailrec 
    def recursion(i: Int): Unit = { 
    if (i < M) { 
     doSomething() 
     recursion(i + 1) 
    } 
    } 

Итак, мой вопрос, какая конструкция лучше использовать относительно Scala стиль и для лучшего производительности? (проблемы, которые я решаю, часто требуют быстрого выполнения, или они не будут переданы)

P.S. Я пишу для этого небольшой тест, и он выглядит так, а tailrec - лучший исполнитель, но не для большого дела. Вы можете посмотреть на него здесь - https://gist.github.com/MysterionRise/5daa63fdbd5d058528fe

ответ

3

Что касается производительности:

Обратите внимание, что если вы хотите писать собственные микро-тесты на JVM, вы должны учитывать many effects - например, производительность одного и того же фрагмента кода будет варьироваться в первой поскольку JVM начинает использовать компиляцию JIT, а затем пытается выполнить различные оптимизации. Чтобы написать надлежащие тесты в Scala, вы можете посмотреть библиотеки, например, ScalaMeter или caliper. Кстати, вторая ссылка сравнивает разные способы петли в Scala, поэтому ее легко адаптировать к вашему прецеденту.

Однако, вообще говоря:

  • Рекурсивное решение хвоста будет оптимизировано для таких же байт-кода, который будет подготовлен эквивалентом в то время цикла, так что два всегда должно быть одинаковыми (если код действительно эквивалентны).
  • цикл for - синтаксический сахар для вызова метода foreach, поэтому два должны быть строго эквивалентными.
  • foreach (или для цикла) несколько медленнее, чем эквивалентный цикл while (или хвостовая рекурсия): связанный с ним benchmark показывает 15x разницу в производительности с 1000 итерациями (хотя это, вероятно, будет зависеть от версии Scala и версия JRE ...). Но эта разница станет пренебрежимо малой, если код внутри цикла займет намного больше времени, чем сам цикл, так что это не значит, что это всегда актуально.

Что касается стиля:

Я полностью согласен с ответом johanandren'S.

3

Я бы оценил их стиль в следующем порядке:

  • для/Еогеаспа
  • хвоста рекурсия
  • императивного While (последний курорт stylewise)

Что касается хвостовой рекурсии и императива производительности, вы получите так же быстро, как и цикл (tailrec на практике станет чем-то очень близким к циклу while) и быстрее a для/foreach.

Замечание: если вы идете на идеоматическую Scala, вы можете попытаться избежать побочного эффекта и вместо этого использовать .map или сделать свою рекурсивную функцию хвоста, а не что-то мутировать.

Смежные вопросы