Как уже говорили другие, разница в том, как вы используете коллекции. Array
в Scala - это то же самое, что примитивный массив Java, []
, который совпадает с примитивным массивом C # []
. Scala достаточно умен, чтобы делать то, что вы просите (а именно, скопировать весь массив с новым элементом в конце), но не настолько умен, чтобы сказать вам, что вам лучше использовать другую коллекцию. Например, если вы просто измените Array
на ArrayBuffer
, это должно быть намного быстрее (сравнимо с C #).
На самом деле, вам бы лучше вообще не использовать циклы for. Одна из сильных сторон библиотеки коллекций Scala заключается в том, что в вашем распоряжении широкий спектр мощных операций. В этом случае вы хотите взять каждый элемент из forms
и преобразовать его в Variant
. Вот что делает map
.
Кроме того, ваш код Scala, похоже, на самом деле не работает.
Если вам нужны все возможные варианты от каждого участника, вы действительно хотите использовать рекурсию. Эта реализация делает то, что вы говорите, что вы хотите:
object test {
def produce[T](input: Array[Array[T]], index: Int = 0): Array[List[T]] = {
if (index >= input.length) Array()
else if (index == input.length-1) input(index).map(elem => List(elem))
else {
produce(input, index+1).flatMap(variant => {
input(index).map(elem => elem :: variant)
})
}
}
def main() {
val arg = Array.tabulate(5,3)((i,j) => i*3+j)
println("Hello, world!")
val start = System.nanoTime
var res = produce(arg)
val stop = System.nanoTime
println("Time elapsed (ms): " + (stop-start)/1000000L)
println("Result length: " + res.length)
println(res.deep)
}
}
Давайте немного распакуем это. Во-первых, мы заменили всю вашу конструкцию начальных вариантов одной инструкцией tabulate
. tabulate
принимает целевой размер (здесь 5x3), а затем функцию, которая отображается из индексов в этот прямоугольник в конечное значение.
Мы также сделали produce
рекурсивной функцией. (Обычно мы делаем это хвостовой рекурсией, но давайте пока все упростим.) Как вы генерируете все варианты? Ну, все варианты ясно (каждая возможность в этой позиции) + (все варианты из более поздних позиций). Поэтому мы записываем это рекурсивно.
Обратите внимание, что если мы рекурсивно строим варианты, как это, все хвосты вариантов заканчиваются одинаково, что делает List
идеальной структурой данных: это односвязный неизменяемый список, поэтому вместо того, чтобы копировать все эти хвосты снова и снова, мы просто указываем на них.
Теперь, как мы на самом деле делаем рекурсию? Ну, если вообще нет данных, нам лучше вернуть пустой массив (т.е. если index
находится за концом массива). Если мы находимся в последнем элементе массива вариантов, мы в основном хотим, чтобы каждый элемент превратился в список длины 1, поэтому мы используем map
, чтобы сделать именно это (elem => List(elem)
). Наконец, если мы не в конце, мы получаем результаты от остальных (то есть produce(input, index+1)
) и создаем варианты для каждого элемента.
Давайте сначала возьмем внутренний цикл: input(index).map(elem => elem :: variant)
. Это берет каждый элемент из вариантов в позиции index
и прикрепляет их к существующему variant
. Так что это даст нам новую партию вариантов. Справедливо, но откуда мы берем новый вариант? Мы производим его из остальной части списка: produce(input, index+1)
, и тогда единственная хитрость в том, что нам нужно использовать flatMap
- это берет каждый элемент, создает из него коллекцию и склеивает все эти коллекции вместе.
Я призываю вас бросать printlns в разные места, чтобы посмотреть, что происходит.
Наконец, обратите внимание, что с вашим размером теста, это фактически незначительный объем работы; Вы не можете точно измерить это, даже если вы переключитесь на использование более точного System.nanoTime
, как я. Вам понадобится что-то вроде tabulate(12,3)
, прежде чем оно станет значительным (500 000 вариантов).