Как использовать Java Collections.shuffle () в массиве Scala? - PullRequest
11 голосов
/ 11 августа 2009

У меня есть массив, который я хочу перестановить случайно. В Java есть метод Collections.shuffle (), который может случайным образом перемешивать элементы списка. Он также может быть использован в массиве:

String[] array = new String[]{"a", "b", "c"};

// Shuffle the array; works because the list returned by Arrays.asList() is backed by the array
Collections.shuffle(Arrays.asList(array));

Я пытался использовать это в массиве Scala, но интерпретатор Scala отвечает длинным ответом:

scala> val a = Array("a", "b", "c")
a: Array[java.lang.String] = Array(a, b, c)

scala> java.util.Collections.shuffle(java.util.Arrays.asList(a))
<console>:6: warning: I'm seeing an array passed into a Java vararg.
I assume that the elements of this array should be passed as individual arguments to the vararg.
Therefore I follow the array with a `: _*', to mark it as a vararg argument.
If that's not what you want, compile this file with option -Xno-varargs-conversion.
       java.util.Collections.shuffle(java.util.Arrays.asList(a))
                                                             ^
<console>:6: error: type mismatch;
 found   : Array[java.lang.String]
 required: Seq[Array[java.lang.String]]
       java.util.Collections.shuffle(java.util.Arrays.asList(a))
                                                             ^

Что именно здесь происходит? Я не хочу компилировать мой код со специальным флагом (-Xno-varargs-преобразование), если это вообще решение, просто из-за этого.

Итак, как мне использовать Java Collections.shuffle () в массиве Scala?

Тем временем я написал свой собственный метод случайного воспроизведения в Scala:

// Fisher-Yates shuffle, see: http://en.wikipedia.org/wiki/Fisher–Yates_shuffle
def shuffle[T](array: Array[T]): Array[T] = {
    val rnd = new java.util.Random
    for (n <- Iterator.range(array.length - 1, 0, -1)) {
        val k = rnd.nextInt(n + 1)
        val t = array(k); array(k) = array(n); array(n) = t
    }
    return array
}

Он перетасовывает массив на месте и для удобства возвращает сам массив.

Ответы [ 3 ]

6 голосов
/ 11 августа 2009

Кажется, что Scala делает что-то отличное от Java, когда дело доходит до varargs. По крайней мере, я не могу заставить этот массив перетасовываться каким-либо образом Предположительно, перестановка в списке будет перетасовывать массив, потому что список поддерживается массивом. Что ж, похоже, что Scala создаст массив new при передаче аргументов vararg в Java, что делает вышеупомянутый пример бесполезным.

scala> val b = java.util.Arrays.asList(a: _*)
b: java.util.List[java.lang.String] = [a, b, c]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [a, b, c]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [c, b, a]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [a, c, b]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [b, a, c]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [a, b, c]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [c, a, b]

Он работает с Ints, несмотря на утверждение, что иначе:

scala> val a = Array(1,2,3)
a: Array[Int] = Array(1, 2, 3)

scala> val b = java.util.Arrays.asList(a: _*)
b: java.util.List[Int] = [1, 2, 3]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(1, 2, 3) [2, 3, 1]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(1, 2, 3) [3, 2, 1]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(1, 2, 3) [3, 2, 1]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(1, 2, 3) [1, 2, 3]

В Scala 2.8 есть более простой способ:

scala> scala.util.Random.shuffle(a)
res32: Sequence[Int] = Array(1, 2, 3)

scala> scala.util.Random.shuffle(a)
res33: Sequence[Int] = Array(2, 1, 3)

scala> scala.util.Random.shuffle(a)
res34: Sequence[Int] = Array(3, 2, 1)

Обратите внимание, что в стиле Scala он не меняет исходный массив.

6 голосов
/ 11 августа 2009
java.util.Collections.shuffle(java.util.Arrays.asList(a:_*))

Для того, чтобы вышеописанное работало правильно, тип элемента a должен быть подклассом scala.AnyRef (эквивалентно java.lang.Object), потому что Arrays.asList () использует массив, переданный в качестве резервного хранилища для результата java .util.List и java.util.List могут содержать только ссылки на объекты (не примитивные значения). *

Это также причина, по которой Collections.shuffle (), которая перетасовывает переданный java.util.List, фактически перетасовывает массив. *

*: см. Примечание ниже

Например:

scala> val a = Array[java.lang.Integer](1, 2, 3) // note the type parameter                  
a: Array[java.lang.Integer] = Array(1, 2, 3)

scala> java.util.Collections.shuffle(java.util.Arrays.asList(a:_*))

scala> a
res43: Array[java.lang.Integer] = Array(1, 3, 2)

scala> java.util.Collections.shuffle(java.util.Arrays.asList(a:_*))

scala> a
res45: Array[java.lang.Integer] = Array(1, 2, 3)

Примечание: Scala 2.7.5 используется для приведенных выше фрагментов кода. Scala 2.8.0 демонстрирует различное поведение, как продемонстрировал Даниэль. Чтобы быть в безопасности, не зависите от факта, что массив перетасовывается, а вместо этого список, возвращаемый из Arrays.asList (), перетасовывается.

scala> val a = Array[java.lang.Integer](1, 2, 3)
a: Array[java.lang.Integer] = Array(1, 2, 3)

scala> val b = java.util.Arrays.asList(a:_*)
b: java.util.List[java.lang.Integer] = [1, 2, 3]

scala> java.util.Collections.shuffle(b)

scala> b
res50: java.util.List[java.lang.Integer] = [2, 1, 3]

scala> java.util.Collections.shuffle(b)

scala> b
res52: java.util.List[java.lang.Integer] = [3, 1, 2]
5 голосов
/ 11 августа 2009

Чтобы ответить "что именно здесь происходит?" часть:

Когда вы говорите java.util.Arrays.asList (a), вы вызываете статический метод Java, который определен для получения переменного числа аргументов (синтаксис vararg ... в Java):

public static <T> List<T> asList(T... a) 

В Scala, когда вы делаете вызов asList, вы передаете один массив [String], а не три строковых параметра. Несмотря на то, что Scala представляет T * как Array [T], вы должны сказать компилятору, что вы действительно хотите передать три параметра, а не один параметр, представляющий собой список из трех вещей.

В Scala есть удобный способ конвертировать ваш массив [String] в String, String, String: вы используете символ _ *, как показано в ответе Уолтера Чанга. Вы можете использовать это всякий раз, когда вы передаете что-то в функцию vararg.

Это описано на страницах 188 и 189 Программирование в Scala.

Вы также увидите _ * в сопоставлении с образцом для соответствия нулю или нескольким элементам в списке.

...