web-dev-qa-db-fra.com

Comment obtenir l'index des éléments lors du mappage d'un tableau dans Scala?

Prenons un exemple de mappage simple:


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

Ce dont j'ai besoin, c'est d'utiliser non pas myFn(s: String): String ici, mais myFn(s: String, n: Int): String, où n serait l'index de s dans a . Dans ce cas particulier, myFn s'attendrait à ce que le deuxième argument soit 0 pour s == "One", 1 pour s == "Two" et 2 pour s == "Three". Comment puis-je atteindre cet objectif?

25
Ivan

Cela dépend si vous voulez la commodité ou la vitesse.

Lent:

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

Plus rapide:

for (i <- a.indices) yield myFn(a(i),i)

{ var i = -1; a.map{ s => i += 1; myFn(s,i) } }

Peut-être plus rapide:

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

Sinon, c'est sûrement:

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

(Dans Scala 2.10.1 avec Java 1.6u37, si "peut-être le plus rapide" est déclaré pour prendre 1x fois le temps pour une opération de chaîne triviale (troncature d'un long chaîne à quelques caractères), puis "lent" prend 2x plus longtemps, "plus rapide" prend 1,3 fois plus longtemps et "sûrement" ne prend que 0,5 fois le temps.)

71
Rex Kerr

Un conseil général: utilisez .iterator méthode généreusement, pour éviter la création de collections intermédiaires, et ainsi accélérer votre calcul. (Uniquement lorsque les exigences de performance l'exigent. Sinon, non.)

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)

Gardez à l'esprit que les itérateurs sont mutables, et donc une fois consommés ne peuvent pas être réutilisés.


Un côté: l'appel map ci-dessus implique le découplage puis l'application de fonction. Cela force l'utilisation de certaines variables locales. Vous pouvez éviter cela en utilisant une sorcellerie d'ordre supérieur - convertissez une fonction régulière en celle qui accepte Tuple, puis passez-la à map.

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

Et ça? Je pense que ça devrait être rapide et c'est joli. Mais je ne suis pas expert en Scala ...

a.foldLeft(0) ((i, x) => {myFn(x, i); i + 1;} )
3
Matthew Saltz