Dado que Scala no tiene for
bucles de estilo Java antiguos con índice,
// does not work
val xs = Array("first", "second", "third")
for (i=0; i<xs.length; i++) {
println("String #" + i + " is " + xs(i))
}
¿Cómo podemos iterar de manera eficiente y sin usar var
's?
Podrías hacer esto
val xs = Array("first", "second", "third")
val indexed = xs zipWithIndex
for (x <- indexed) println("String #" + x._2 + " is " + x._1)
pero la lista se recorre dos veces, no muy eficiente.
while
bucle. Según recuerdo, hace algunos años hubo un debate sobre si Scala debería heredar elfor(;;)
bucle de Java , y se decidió que el beneficio no era suficiente para justificar la complejidad añadida.Respuestas:
Mucho peor que atravesar dos veces, crea una matriz intermedia de pares. Puede utilizar
view
. Cuando lo hagacollection.view
, puede pensar que las llamadas posteriores actúan de manera perezosa durante la iteración. Si desea recuperar una colección adecuada y completamente realizada, llameforce
al final. Aquí eso sería inútil y costoso. Así que cambia tu código afor((x,i) <- xs.view.zipWithIndex) println("String #" + i + " is " + x)
fuente
List
la colección más utilizada en Scala), y no solo en ellas. Mira laapply
operación aquí . En una colección similar a una lista vinculada, cada acceso a un elemento por índice da como resultado un recorrido de la lista.Se ha mencionado que la Scala hace tener sintaxis para
for
los bucles:for (i <- 0 until xs.length) ...
o simplemente
for (i <- xs.indices) ...
Sin embargo, también pediste eficiencia. Resulta que la Scala
for
sintaxis es en realidad azúcar sintáctico para los métodos de orden superior, tales comomap
,foreach
, etc. Por lo tanto, en algunos casos, estos bucles pueden ser ineficientes, por ejemplo, cómo optimizar para-comprensiones y bucles en Scala?(La buena noticia es que el equipo de Scala está trabajando para mejorar esto. Aquí está el problema en el rastreador de errores: https://issues.scala-lang.org/browse/SI-4633 )
Para una máxima eficiencia, se puede usar un
while
bucle o, si insiste en eliminar los usos devar
, la recursividad de cola:import scala.annotation.tailrec @tailrec def printArray(i: Int, xs: Array[String]) { if (i < xs.length) { println("String #" + i + " is " + xs(i)) printArray(i+1, xs) } } printArray(0, Array("first", "second", "third"))
Tenga en cuenta que la anotación opcional
@tailrec
es útil para garantizar que el método sea realmente recursivo. El compilador de Scala traduce las llamadas recursivas de cola en el código de bytes equivalente a los bucles while.fuente
xs
es de cualquier tipo de lista enlazada (como la ampliamente utilizadaList
), acceder a sus elementos por índice comoxs(i)
será lineal y, por lo tanto, elfor (i <- xs.indices) println(i + " : " + xs(i))
rendimiento será peor que inclusofor((x, i) <- xs.zipWithIndex) println(i + " : " + x)
, ya que resultará en mucho más que dos travesías bajo el capó. Por lo tanto, la respuesta de @didierd sugiriendo usar vistas debe aceptarse como la más general y la más idiomática, IMO.view
se usa a, este nivel incluso alto de abstracción ejercerá más presión sobre el montón y GC. En mi experiencia, a menudo se obtiene un factor de 10 en el rendimiento al evitar la asignación de montones en código numérico.Una forma más:
scala> val xs = Array("first", "second", "third") xs: Array[java.lang.String] = Array(first, second, third) scala> for (i <- xs.indices) | println(i + ": " + xs(i)) 0: first 1: second 2: third
fuente
En realidad, scala tiene bucles antiguos de estilo Java con índice:
scala> val xs = Array("first","second","third") xs: Array[java.lang.String] = Array(first, second, third) scala> for (i <- 0 until xs.length) | println("String # " + i + " is "+ xs(i)) String # 0 is first String # 1 is second String # 2 is third
Donde
0 until xs.length
o0.until(xs.length)
es unRichInt
método que devuelveRange
adecuado para bucles.Además, puede probar el bucle con
to
:scala> for (i <- 0 to xs.length-1) | println("String # " + i + " is "+ xs(i)) String # 0 is first String # 1 is second String # 2 is third
fuente
xs(i)
en listas eleva la complejidad a O (n ^ 2)¿Qué tal esto?
val a = Array("One", "Two", "Three") a.foldLeft(0) ((i, x) => {println(i + ": " + x); i + 1;} )
Salida:
0: One 1: Two 2: Three
fuente
Hacer un bucle en Scala es bastante simple. Cree cualquier matriz de su elección, por ej.
val myArray = new Array[String](3) myArray(0)="0"; myArray(1)="1"; myArray(2)="2";
Tipos de bucles,
for(data <- myArray)println(data) for (i <- 0 until myArray.size) println(i + ": " + myArray(i))
fuente
De hecho, llamar
zipWithIndex
a una colección la atravesará y también creará una nueva colección para los pares. Para evitar esto, puede simplemente llamarzipWithIndex
al iterador para la colección. Esto solo devolverá un nuevo iterador que realiza un seguimiento del índice mientras se itera, por lo que sin crear una colección adicional o un recorrido adicional.Así es como
scala.collection.Iterator.zipWithIndex
se implementa actualmente en 2.10.3:def zipWithIndex: Iterator[(A, Int)] = new AbstractIterator[(A, Int)] { var idx = 0 def hasNext = self.hasNext def next = { val ret = (self.next, idx) idx += 1 ret } }
Esto debería ser incluso un poco más eficaz que crear una vista de la colección.
fuente
No hay nada en stdlib que lo haga por usted sin crear basura de tupla, pero no es demasiado difícil escribir la suya propia. Desafortunadamente, nunca me he molestado en averiguar cómo hacer el CanBuildFrom implícito de la danza de lluvia adecuada para hacer esas cosas genéricas en el tipo de colección a la que se aplican, pero si es posible, estoy seguro de que alguien nos aclarará. :)
def foreachWithIndex[A](as: Traversable[A])(f: (Int,A) => Unit) { var i = 0 for (a <- as) { f(i, a) i += 1 } } def mapWithIndex[A,B](in: List[A])(f: (Int,A) => B): List[B] = { def mapWithIndex0(in: List[A], gotSoFar: List[B], i: Int): List[B] = { in match { case Nil => gotSoFar.reverse case one :: more => mapWithIndex0(more, f(i, one) :: gotSoFar, i+1) } } mapWithIndex0(in, Nil, 0) } // Tests.... @Test def testForeachWithIndex() { var out = List[Int]() ScalaUtils.foreachWithIndex(List(1,2,3,4)) { (i, num) => out :+= i * num } assertEquals(List(0,2,6,12),out) } @Test def testMapWithIndex() { val out = ScalaUtils.mapWithIndex(List(4,3,2,1)) { (i, num) => i * num } assertEquals(List(0,3,4,3),out) }
fuente
Algunas formas más de iterar:
foreach, y similar, map, que devolvería algo (los resultados de la función, que es, para println, Unit, por lo que una lista de unidades)
scala> val lens = for (x <- xs) yield (x.length) lens: Array[Int] = Array(5, 6, 5)
trabajar con los elementos, no con el índice
scala> ("" /: xs) (_ + _) res21: java.lang.String = firstsecondthird
plegable
def ijIter (i: Int = 0, j: Int = 0, carry: Int = 0) : Int = if (i + j >= 100) carry else ijIter (i+2*j, j+i+2, carry / 3 + 2 * i - 4 * j + 10)
La parte de acarreo es solo un ejemplo, para hacer algo con i y j. No tiene por qué ser un Int.
para cosas más simples, más cercanas a los bucles for habituales:
scala> (1 until 4) res43: scala.collection.immutable.Range with scala.collection.immutable.Range.ByOne = Range(1, 2, 3) scala> (0 to 8 by 2) res44: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8) scala> (26 to 13 by -3) res45: scala.collection.immutable.Range = Range(26, 23, 20, 17, 14)
o sin orden:
List (1, 3, 2, 5, 9, 7).foreach (print)
fuente
Tengo los siguientes enfoques
object HelloV2 { def main(args: Array[String]) { //Efficient iteration with index in Scala //Approach #1 var msg = ""; for (i <- args.indices) { msg+=(args(i)); } var msg1=""; //Approach #2 for (i <- 0 until args.length) { msg1 += (args(i)); } //Approach #3 var msg3="" args.foreach{ arg => msg3 += (arg) } println("msg= " + msg); println("msg1= " + msg1); println("msg3= " + msg3); } }
fuente
Una forma sencilla y eficiente, inspirada en la implementación de
transform
en SeqLike.scalavar i = 0 xs foreach { el => println("String #" + i + " is " + xs(i)) i += 1 }
fuente
Las soluciones propuestas adolecen del hecho de que iteran explícitamente sobre una colección o rellenan la colección en una función. Es más natural ceñirse a los modismos habituales de Scala y poner el índice dentro de los métodos usuales map o foreach. Esto se puede hacer mediante la memorización. El código resultante podría verse así
He aquí una forma de lograr este propósito. Considere la siguiente utilidad:
object TraversableUtil { class IndexMemoizingFunction[A, B](f: (Int, A) => B) extends Function1[A, B] { private var index = 0 override def apply(a: A): B = { val ret = f(index, a) index += 1 ret } } def doIndexed[A, B](f: (Int, A) => B): A => B = { new IndexMemoizingFunction(f) } }
Esto ya es todo lo que necesitas. Puede aplicar esto, por ejemplo, de la siguiente manera:
import TraversableUtil._ List('a','b','c').map(doIndexed((i, char) => char + i))
que resulta en la lista
List(97, 99, 101)
De esta manera, puede usar las funciones habituales de Traversable a expensas de envolver su función efectiva. ¡Disfrutar!
fuente