Is it possible to create a custom control structure with several code blocks, in the fashion of before { block1 } then { block2 } finally { block3 }
? The question is about the sugar part only - I know the functionality can be easily achieved by passing the three blocks to a method, like doInSequence(block1, block2, block3)
.
A real life example. For my testing utilities I'd like to create a structure like this:
getTime(1000) {
// Stuff I want to repeat 1000 times.
} after { (n, t) =>
println("Average time: " + t / n)
}
EDIT:
Finally I came up with this solution:
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)开发者_C百科 {
println(i)
i += 1
Thread.sleep(10)
} after { (n, t) =>
println("Average time: " + t.toDouble / n)
}
The output is:
1
2
3
...
99
100
Average time: 10.23
It is mostly based on the answer by Thomas Lockney, I just added the companion object to be able to import MyTimer._
Thank you all, guys.
General principle. You can of course have f take parameters as well. (Note that the name of the methods have no meaning in this example.)
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 = Foo@1f16e6e
If you want these blocks to appear in the specific order, this change to Knut Arne Vedaa's answer would work:
class Foo1 {
def before(f: => Unit) = { f; new Foo2 }
}
class Foo2 {
def then(f: => Unit) = { f; new Foo3 }
}
...
For your given example, the key would be to have the return type of getTime
have the after
method on it. Depending on the context, you could use a single class or trait which wraps up both methods. Here's a very simplified example of how you might approach it:
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
}
}
It is not possible to have a "split" method, but you can emulate it.
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>
精彩评论