2011-01-01 9 views
6

¿Es posible crear una estructura de control personalizada con varios bloques de código, a la manera de before { block1 } then { block2 } finally { block3 }? La pregunta es sobre la parte de azúcar solamente: sé que la funcionalidad se puede lograr fácilmente pasando los tres bloques a un método, como doInSequence(block1, block2, block3).Scala: estructuras de control personalizadas con varios bloques de código

Un ejemplo de la vida real. Para mis utilidades de prueba que me gustaría crear una estructura como esta:

getTime(1000) { 
    // Stuff I want to repeat 1000 times. 
} after { (n, t) => 
    println("Average time: " + t/n) 
} 

EDITAR:

Finalmente se me ocurrió con esta solución:

object MyTimer { 
    def getTime(count: Int)(action : => Unit): MyTimer = { 
    val start = System.currentTimeMillis() 
    for(i <- 1 to count) { action } 
    val time = System.currentTimeMillis() - start 
    new MyTimer(count, time) 
    } 
} 

class MyTimer(val count: Int, val time: Long) { 
    def after(action: (Int, Long) => Unit) = { 
    action(count, time) 
    } 
} 

// Test 
import MyTimer._ 

var i = 1 
getTime(100) { 
    println(i) 
    i += 1 
    Thread.sleep(10) 
} after { (n, t) => 
    println("Average time: " + t.toDouble/n) 
} 

La salida es:

1 
2 
3 
... 
99 
100 
Average time: 10.23 

Se basa principalmente en la respuesta por Thomas Lockney, acabo de agregar el objeto complementario para poder import MyTimer._

Gracias a todos, muchachos.

Respuesta

3

Para su ejemplo dado, la clave sería tener el tipo de devolución getTime con el método after. Dependiendo del contexto, puede usar una única clase o rasgo que envuelva ambos métodos. Aquí hay un ejemplo muy simplificado de cómo puede abordarlo:

class Example() { 
    def getTime(x: Int)(f : => Unit): Example = { 
    for(i <- 0 to x) { 
     // do some stuff 
     f 
     // do some more stuff 
    } 
    // calculate your average 
    this 
    } 
    def after(f: (Int, Double) => Unit) = { 
    // do more stuff 
    } 
} 
13

Principio general. Por supuesto, también puedes tener parámetros para tomar. (. Tenga en cuenta que el nombre de los métodos no tienen ningún significado en este ejemplo)

scala> class Foo { 
    | def before(f: => Unit) = { f; this } 
    | def then(f: => Unit) = { f; this } 
    | def after(f: => Unit) = { f; this } 
    | } 
defined class Foo 

scala> object Foo { def apply() = new Foo } 
defined module Foo 

scala> Foo() before { println("before...") } then { 
    | println("then...") } after { 
    | println("after...") } 
before... 
then... 
after... 
res12: Foo = [email protected] 
+0

Eso es embarazosamente simple :). Gracias –

8

Si desea que estos bloques a aparecer en el orden específico, este cambio a la respuesta de Knut Arne Vedaa funcionaría:

class Foo1 { 
    def before(f: => Unit) = { f; new Foo2 } 
} 

class Foo2 { 
    def then(f: => Unit) = { f; new Foo3 } 
} 

... 
1

No es posible tener un método "dividido", pero puede emularlo.

class Finally(b: => Unit, t: => Unit) { 
    def `finally`(f: => Unit) = { 
     b 
     try { t } finally { f } 
    } 
} 

class Then(b: => Unit) { 
    def `then`(t: => Unit): Finally = new Finally(b, t) 
} 

def before(b: => Unit): Then = new Then(b) 

scala> before { println("Before") } `then` { 2/0 } `finally` { println("finally") } 
Before 
finally 
[line4.apply$mcV$sp] (<console>:9) 
(access lastException for the full trace) 
scala> 
Cuestiones relacionadas