Scala: настраиваемые структуры управления с несколькими блоками кода

Можно ли создать пользовательскую структуру управления с несколькими блоками кода по типу before { block1 } then { block2 } finally { block3 }? Вопрос касается только части сахара - я знаю, что функциональность может быть легко достигнута путем передачи трех блоков методу, например doInSequence(block1, block2, block3).

Пример из реальной жизни. Для моих утилит тестирования я хотел бы создать такую ​​​​структуру:

getTime(1000) {
  // Stuff I want to repeat 1000 times.
} after { (n, t) => 
  println("Average time: " + t / 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)
}

Результат:

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

В основном это основано на ответе Томаса Локни, я просто добавил объект-компаньон, чтобы иметь возможность import MyTimer._

Спасибо всем, ребята.


person Vilius Normantas    schedule 01.01.2011    source источник


Ответы (4)


Для вашего примера ключом будет иметь возвращаемый тип getTime с методом after. В зависимости от контекста вы можете использовать один класс или трейт, который объединяет оба метода. Вот очень упрощенный пример того, как вы можете подойти к этому:

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
  }
}
person Thomas Lockney    schedule 01.01.2011

Основной принцип. Конечно, вы можете использовать и параметры f. (Обратите внимание, что имена методов в этом примере не имеют значения.)

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
person Knut Arne Vedaa    schedule 01.01.2011
comment
Это до безобразия просто :). Спасибо - person Vilius Normantas; 03.01.2011

Если вы хотите, чтобы эти блоки отображались в определенном порядке, это изменение ответа Кнута Арне Веды будет работать:

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

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

...
person Alexey Romanov    schedule 01.01.2011

Метод «разделения» невозможен, но вы можете подражать ему.

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>
person Daniel C. Sobral    schedule 02.01.2011