2011-09-08 14 views
9

Entonces, mientras trabajaba en "Scala para el impaciente", me pregunté: ¿Se puede usar un bucle de Scala sin secuencia?Para bucle en scala sin secuencia?

Por ejemplo, hay un ejercicio en el libro que le pide que cree un objeto de contador que no puede incrementarse más allá de Integer.MAX_VALUE. Con el fin de probar mi solución, escribí el siguiente código:

var c = new Counter 
for(i <- 0 to Integer.MAX_VALUE) c.increment() 

Esto arroja un error: secuencias no pueden contener más elementos Int.MaxValue. Me parece que eso significa que Scala está asignando y poblando por primera vez un objeto de secuencia, con los valores 0 a Integer.MaxValue, y luego haciendo un bucle foreach en ese objeto de secuencia.

que se dan cuenta de que podía hacer esto en su lugar:

var c = new Counter 
while(c.value < Integer.MAX_VALUE) c.increment() 

pero ¿hay alguna manera de hacer un estilo tradicional C para el lazo con la sentencia for?

Respuesta

17

De hecho, en realidad no 0 to N pueblan cualquier cosa con números enteros 0-N. En su lugar, crea una instancia de scala.collection.immutable.Range, que aplica sus métodos a todos los enteros generados sobre la marcha.

El error que se encontró es solo porque tiene que poder ajustar el número de elementos (si existen o no) en la parte positiva de Int para mantener el contrato para el método length. 1 to Int.MaxValue funciona bien, al igual que 0 until Int.MaxValue. Y lo último es lo que su bucle while está haciendo de todos modos (to incluye el punto final derecho, until lo omite).

De todos modos, como la Scala for es una criatura muy diferente (mucho más genérica) que la C for, la respuesta corta es no, no se puede hacer exactamente lo mismo. Pero probablemente puedas hacer lo que quieras con for (aunque tal vez no tan rápido como quieras, ya que hay una cierta penalización de rendimiento).

4

Sí y no, depende de lo que esté pidiendo. Si estás preguntando si se puede iterar sobre una secuencia de enteros sin tener que construir esa primera secuencia, entonces sí se puede, por ejemplo, utilizando corrientes:

def fromTo(from : Int, to : Int) : Stream[Int] = 
    if(from > to) { 
    Stream.empty 
    } else { 
    // println("one more.") // uncomment to see when it is called 
    Stream.cons(from, fromTo(from + 1, to)) 
    } 

continuación:

for(i <- fromTo(0, 5)) println(i) 

Escribir su propio iterador definiendo hasNext y el siguiente es otra opción.

Si está preguntando si puede usar la sintaxis 'para' para escribir un bucle 'nativo', es decir, un bucle que funciona incrementando un entero nativo en lugar de iterar sobre valores producidos por una instancia de un objeto, entonces la respuesta es, por lo que yo sé, no. Como sabrá, "para" las comprensiones son azúcar sintáctico para una combinación de llamadas a flatMap, filter, map y foreach (todas definidas en el rasgo FilterMonadic), dependiendo de la anidación de los generadores y sus tipos. Puede intentar compilar algún bucle e imprimir su representación intermedia del compilador con

scalac -Xprint:refchecks 

para ver cómo se expanden.

+0

Guau, una respuesta desafiante, pero una buena. Estoy aprendiendo Scala, así que has usado muchos términos con los que apenas estoy familiarizado, pero gracias. –

+0

La definición de 'fromTo' se puede simplificar aún más mediante el uso del método' iterate' en el objeto complementario 'Stream' (o' Iterator'). Algo en la línea de: 'def fromTo (desde: Int, to: Int) = Stream.iterate (from, to - from) (_ + 1)'. Pero usar 'from to to' es más idiomático y logra lo mismo. –

2

Hay un montón de estos por ahí, pero no me puedo molestar en buscarlos en Google en este momento. La siguiente es bastante canónica:

@scala.annotation.tailrec 
def loop(from: Int, until: Int)(f: Int => Unit): Unit = { 
    if (from < until) { 
    f(from) 
    loop(from + 1, until)(f) 
    } 
} 

loop(0, 10) { i => 
    println("Hi " + i) 
} 
5

Wow, algunas respuestas técnicas agradables para una pregunta simple (que es bueno!), Pero en caso de que alguien está sólo en busca de una respuesta sencilla:

//start from 0, stop at 9 inclusive 
for (i <- 0 until 10){ 
    println("Hi " + i) 
} 

//or start from 0, stop at 9 inclusive 
for (i <- 0 to 9){ 
    println("Hi " + i) 
} 

Como Rex señaló, "a" incluye el derecho punto final, "hasta" omite.

Cuestiones relacionadas