2009-08-11 37 views
11

Tengo una matriz que quiero permuta aleatoriamente. En Java, hay un método Collections.shuffle() que puede mezclar aleatoriamente los elementos de una Lista. Puede ser utilizado en una gran variedad demasiado:¿Cómo usar Java Collections.shuffle() en una matriz de Scala?

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

He intentado utilizar esto en una matriz Scala, pero el intérprete Scala responde con una respuesta larga:

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

¿Qué es exactamente que está pasando aquí? No quiero compilar mi código con una bandera especial (-Xno-varargs-conversion), si esa es la solución, solo por eso.

Entonces, ¿cómo utilizo Collections.shuffle() de Java en una matriz de Scala?

escribí mi propio método shuffle en Scala en el ínterin:

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

Se baraja la matriz en su lugar, y devuelve la matriz en sí para mayor comodidad.

+0

Tenga en cuenta que mezclar de esta manera solo funciona para matrices de tipos de referencia. – starblue

+0

@starblue: Sí, la versión de Java funciona solo para matrices de tipos de referencia. Mi propio método Scala shuffle() también funciona con matrices Scala con primitivas. – Jesper

Respuesta

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

Por lo anterior para funcionar correctamente, de un tipo de elemento tiene que ser una subclase de scala.AnyRef (equivalente a java.lang.Object) porque Arrays.asList() utiliza la matriz pasada como el almacén de respaldo para el resultado, java.util.List y java.util.List solo pueden contener referencias de objetos (no valores primitivos). *

Ese es también el motivo por el que Collections.shuffle() que baraja el java.util transferido .List realmente mezcló la matriz. *

*: Consulte la nota siguiente

Por ejemplo:

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) 

Nota: Scala 2.7.5 se utiliza para los fragmentos de código anteriores. Scala 2.8.0 exhibe comportamientos diferentes como demostró Daniel. Para estar seguro, no dependa del hecho de que la matriz se mezcle, sino que la lista que se devuelve desde Arrays.asList() se mezcle.

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] 
+0

Gracias. Pero eso no funciona para una matriz de Scala con enteros: da como resultado una ClassCastException, porque la matriz no se puede convertir a una matriz de objetos de Java. – Jesper

+0

@Jesper Para que Collections.shuffle() funcione, asegúrese de que la matriz contenga referencias a objetos y no valores primitivos. (scala.Int -> java.lang.Integer, scala.Long -> java.lang.Long, etc.) He editado la respuesta para enfatizar este punto. –

5

Para responder al "¿Qué está pasando exactamente aquí?" parte:

Cuando dice java.util.Arrays.asList (a) que está llamando a un método estático de Java que se define a tomar un número variable de argumentos (la sintaxis vararg ... en Java):

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

En Scala cuando haces la llamada a asList estás pasando un solo Array [String] y no tres parámetros de cadena. Aunque Scala representa T * como Array [T], necesita decirle al compilador que realmente quiere pasar tres parámetros, en lugar de un solo parámetro que es una Lista de tres cosas.

Scala tiene una manera conveniente de convertir su Array [String] a String, String, String: utiliza el símbolo _ * como se muestra en la respuesta de Walter Chang. Puedes usarlo siempre que pases algo a una función vararg.

Esto se describe en las páginas 188 y 189 de Programación en Scala.

También verá _ * en la coincidencia de patrones para que coincida con cero o más elementos en una lista.

6

Parece que Scala está haciendo algo diferente de Java cuando se trata de varargs. Al menos, no puedo mezclar esa matriz de ninguna manera. Supuestamente, la mezcla de la lista mezclaría la matriz porque la lista está respaldada por una matriz. Bueno, parece que Scala creará una nueva matriz al pasar argumentos vararg a Java, lo que hace inútil el ejemplo anterior.

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] 

Lo hace obras con intercepciones, a pesar de la afirmación de otro modo, sin embargo:

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] 

En Scala 2.8, hay una manera más sencilla:

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) 

Tenga en cuenta que, de una manera Scala , no cambia la matriz original.

+0

Daniel, si hago esto: val a = Matriz ("a", "b", "c") java.util.Collections.shuffle (java.util.Arrays.asList (a: _ *)) luego mi matriz de Scala se baraja. Y si hago una matriz de Ints en lugar de Strings, obtengo una ClassCastException. – Jesper

+0

Y gracias por la sugerencia sobre Scala 2.8, por lo que habrá un método shuffle() en la biblioteca en 2.8. Por supuesto, devuelve una nueva matriz, esa es la forma funcional típica de programación. – Jesper

+0

@Daniel Creo que estás usando 2.8.0 repl. Pruebe sus ejemplos en 2.7.5 y verá diferencias con sus resultados. Aparentemente, 2.8.0 genera un nuevo Seq cuando ve ": _ *", pero 2.7.5 simplemente envuelve un Seq alrededor de la matriz. –

Cuestiones relacionadas