2010-06-14 7 views
10

Soy Peter Pilgrim. Vi a Martin Odersky crear una abstracción de control en Scala. Sin embargo, todavía no puedo repetirlo dentro de IntelliJ IDEA 9. ¿Es IDE?¿Cómo se puede hacer Scala Control Abstraction in Repeat Until?

package demo 

class Control { 

    def repeatLoop (body: => Unit) = new Until(body) 

    class Until(body: => Unit) { 
    def until(cond: => Boolean) { 
     body; 
     val value: Boolean = cond; 
     println("value="+value) 
     if (value) repeatLoop(body).until(cond) 
     // if (cond) until(cond) 
    } 
    } 

    def doTest2(): Unit = { 
    var y: Int = 1 
    println("testing ... repeatUntil() control structure") 
    repeatLoop { 
     println("found y="+y) 
     y = y + 1 
    } 
    { until (y < 10) } 
    } 

} 

El mensaje de error lee:

Information:Compilation completed with 1 error and 0 warnings
Information:1 error
Information:0 warnings
C:\Users\Peter\IdeaProjects\HelloWord\src\demo\Control.scala
Error:Error:line (57)error: Control.this.repeatLoop({
scala.this.Predef.println("found y=".+(y));
y = y.+(1)
}) of type Control.this.Until does not take parameters
repeatLoop {

En la función de curry el cuerpo puede ser pensado para volver una expresión (el valor de y + 1), sin embargo el parámetro de cuerpo declaración de repeatUntil dice claramente esta puede ser ignorado o no?

¿Qué significa el error?

Respuesta

10

Aquí hay una solución sin el StackOverflowError.

scala> class ConditionIsTrueException extends RuntimeException 
defined class ConditionIsTrueException 

scala> def repeat(body: => Unit) = new { 
|  def until(condition: => Boolean) = { 
|  try { 
|   while(true) { 
|   body 
|   if (condition) throw new ConditionIsTrueException 
|   } 
|  } catch { 
|   case e: ConditionIsTrueException => 
|  } 
|  
|  } 
| } 
repeat: (body: => Unit)java.lang.Object{def until(condition: => Boolean): Unit} 

scala> var i = 0    
i: Int = 0 

scala> repeat { println(i); i += 1 } until(i == 3) 
0 
1 
2 

scala> repeat { i += 1 } until(i == 100000)  

scala> repeat { i += 1 } until(i == 1000000) 

scala> repeat { i += 1 } until(i == 10000000) 

scala> repeat { i += 1 } until(i == 100000000) 

scala> 

Según Jesper y Rex Kerr aquí hay una solución sin la excepción.

def repeat(body: => Unit) = new { 
    def until(condition: => Boolean) = { 
    do { 
     body 
    } while (!condition) 
    } 
} 
+0

Agradable. Me gusta el objeto de función anónima. –

+1

se llama tipo estructural –

+4

¿Por qué intentar/atrapar cuando puedes 'do {body} while (! Condition)'? –

8

No es necesario el segundo par de llaves, el uso debe ser:

repeatLoop (x) until (cond) //or... 
repeatLoop {x} until {cond} 

Y no:

repeatLoop {x} { until(cond) } //EXTRA PAIR OF BRACES 

El error significa que Scala piensa que está intentando llamar un método con una firma algo así como:

def repeatLoop(x: => Unit)(something: X) //2 parameter lists 

Y no se puede encontrar tal método. Está diciendo "repeatLoop (cuerpo)" no toma los parámetros. Un código completa lista para la solución probablemente se parece algo un poco más como:

object Control0 { 
    def repeatLoop(body: => Unit) = new Until(body) 

    class Until(body: => Unit) { 
    def until(cond: => Boolean) { 
     body; 
     val value: Boolean = cond; 

     if (value) repeatLoop(body).until(cond) 
    } 
    } 


    def main(args: Array[String]) { 
    var y: Int = 1 
    println("testing ... repeatUntil() control structure") 
    repeatLoop { 
     println("found y=" + y) 
     y += 1 
    }.until(y < 10) 
    } 
} 

Hay dos observaciones útiles para hacer aquí:

  1. La solución no es recursiva de cola y dará lugar a un StackOverflowError para iteraciones largas (prueba while (y < 10000))
  2. El until parece ser el camino equivocado para mí (sería más natural detenerse cuando la condición se vuelve verdadera, no continuar mientras sea verdadera).
+0

Ah, ya veo. Muchas gracias.El cambio de código funciona de hecho 1. Punto tomado 2. Fue el ejercicio de Martin Odersky. Sí, escribes la abstracción de control al revés con '' whileLoop (body: => Boolean) '' es más fácil. Creo que también puedes hacer que la cola recursiva sea más fácil. He intentado replicar la estructura "repeat" ... "until", por lo que no quería el extra (".") Entre el tipo de función "repeatLoop" y el método del objeto "funtion" hasta ". Si intenta hacer esto, entonces hay un error de compilación: valor no encontrado hasta que. –

6

Cómo sobre un un trazador de líneas para repetir hasta.

def repeat(b: => Unit) = new AnyRef {def until(c: => Boolean) {b; while (! c) b}} 

que, por ejemplo, da: -

scala> repeat { 
    | println("i = "+i) 
    | i+=1 
    | } until (i >= 10) 
i = 0 
i = 1 
i = 2 
i = 3 
i = 4 
i = 5 
i = 6 
i = 7 
i = 8 
i = 9 
4

Como arriba todavía recursiva :)

def repeat(b: => Unit) = new {def until(c: => Boolean) = { b; if (c) until(c) }} 

var i = 0 
repeat { 
    println(i) 
    i+=1 
} until (i < 10) 

Es @tailrec optimizado también.

Llove scala :)

+0

¿No olvidó el tipo de devolución para la def hasta? – Bas

+0

Vaya, gracias. Eres como el compilador :) –

Cuestiones relacionadas