2012-02-04 9 views
24

Vamos a considerar un ejemplo sencillo de mapeo:¿Cómo obtener el índice del elemento al mapear una matriz en Scala?


    val a = Array("One", "Two", "Three") 
    val b = a.map(s => myFn(s)) 

Lo que necesito es utilizar no myFn(s: String): String aquí, pero myFn(s: String, n: Int): String, donde n sería el índice de s en a. En este caso particular myFn esperaría que el segundo argumento fuera 0 para s == "Uno", 1 para s == "Dos" y 2 para s == "Tres". ¿Cómo puedo conseguir esto?

Respuesta

66

Depende si desea conveniencia o velocidad.

lento:

a.zipWithIndex.map{ case (s,i) => myFn(s,i) } 

más rápido:

Posiblemente más rápido:

Array.tabulate(a.length){ i => myFn(a(i),i) } 

Si no es así, esto sin duda es:

val b = new Array[Whatever](a.length) 
var i = 0 
while (i < a.length) { 
    b(i) = myFn(a(i),i) 
    i += 1 
} 

(En Scala 2.10.1 con Java 1.6u37, si se declara "posiblemente el más rápido" para tomar 1x vez para una operación de cadena trivial (truncamiento de una cadena larga a unos pocos caracteres), entonces "lento" tarda 2 veces más, "más rápido" cada toma 1.3x más larga, y "seguramente" solo toma 0.5x el tiempo.)

+17

Me encanta leer sus respuestas en materia de velocidad, pero puede ser tan deprimente a veces ... :-) Gracias –

+0

. Una buena selección. – Ivan

+0

¿Cómo se compara la velocidad de 'a.view.zipWithIndex.map {case (s, i) => myFn (s, i)}' con sus soluciones? – gzm0

4

Un consejo general: utilice el método .iterator generosamente, para evitar la creación de colecciones intermedias, y así acelerar su cálculo. (Sólo cuando los requisitos de rendimiento exigen. O de lo contrario no lo hacen.)

scala> def myFun(s: String, i: Int) = s + i 
myFun: (s: String, i: Int)java.lang.String 

scala> Array("nami", "zoro", "usopp") 
res17: Array[java.lang.String] = Array(nami, zoro, usopp) 

scala> res17.iterator.zipWithIndex 
res19: java.lang.Object with Iterator[(java.lang.String, Int)]{def idx: Int; def idx_=(x$1: Int): Unit} = non-empty iterator 

scala> res19 map { case (k, v) => myFun(k, v) } 
res22: Iterator[java.lang.String] = non-empty iterator 

scala> res22.toArray 
res23: Array[java.lang.String] = Array(nami0, zoro1, usopp2) 

Tenga en cuenta que los iteradores son mutables, y por lo tanto una vez que se consume no puede ser utilizado de nuevo.


Un aparte: La llamada map anterior implica de-tupling y luego aplicación de función. Esto fuerza el uso de algunas variables locales. Puede evitar eso usando alguna hechicería de orden superior: convierta una función regular a la que está aceptando una tupla, y luego páselo a map.

scala> Array("nami", "zoro", "usopp").zipWithIndex.map(Function.tupled(myFun)) 
res24: Array[java.lang.String] = Array(nami0, zoro1, usopp2) 
+0

"los iteradores son mutables, y por lo tanto, una vez consumidos no se pueden volver a utilizar" - Una vez pasé un tiempo depurando por qué un programa no funciona para encontrar un iterador que se vacíe una vez utilizado :-) – Ivan

3

¿Qué tal esto? Creo que debería ser rápido y bonito. Pero no soy un experto en la velocidad Scala ...

a.foldLeft(0) ((i, x) => {myFn(x, i); i + 1;}) 
Cuestiones relacionadas