2

我有一个有状态的算法,它逐渐接受输入并逐渐产生输出。输入和输出在数量上不相关;即一个输入可能产生零个或多个输出。

我正试图将它变成EnumerateePlay Framework 中的一个,但我很难开始。

我的算法具有本地可变状态和同步操作,看起来像这样

var state = 'foo
var i = input()
while (i != null) {
    if (state == 'foo && i >= 0) {
        // 2 outputs, and change state
        output(i - 2)
        output(i * 3)
        state = 'bar
    } else if (state == 'foo) {
        // error
        error("expected >= 0")
    } else if (state == 'bar) {
        // 0 outputs, and change state
        state = 'foo
    }
    ... // etc
    i = input()
}
if (state != 'bar) { 
    error("unexpected end")
}

我研究了map,filter等的实现Enumeratee.scala,我有点理解它们。但是我很难看到如何编写我自己的更复杂的实现。

您能否描述/演示我如何将此算法转换为Enumeratee?

4

1 回答 1

2

您唯一需要实现的是applyOn方法:

def applyOn[A](inner: Iteratee[To, A]): Iteratee[From, Iteratee[To, A]] = ...

其他一切都在 trait 中实现。

在创建迭代器时,我发现递归是最重要的技巧;这是一种类似延续的风格,不是返回任何东西,而是每一步计算它需要计算的东西,然后再次调用它。所以你的状态应该成为一个函数参数:

def next[A](inner: Iteratee[To, A], i: Input[From], state: Symbol)
  : Iteratee[From, A] =
  i match {
    case Input.El(e) =>
      if(state == 'foo && e >= 0) {
        val nextInner = Iteratee.flatten {
          inner.feed(Input.El(e - 2)) flatMap {_.feed(Input.El(e * 3))}
        }
        val nextState = 'bar
        Cont {k => next(nextInner, k, nextState)}
      } else if(state == 'foo)
        Error("expected >=0", i)
      else if(state == 'bar)
        next(inner, i, 'foo)
      ...
    case _ =>
      //pass through Empty or Eof
      Iteratee.flatten(inner.feed(i))
  }
def applyOn[A](inner: Iteratee[To, A]): Iteratee[From, Iteratee[To, A]] =
  Cont {i => next(inner, i, 'foo)}

请注意我们如何返回对 的直接递归调用next,或最终将(相互)递归调用的延续next

我已经Input明确地将周围传递给每个调用,而更优雅的方法可能会在延续中处理它(并且可能applyOn直接实现而不是将其作为包装方法),但希望这可以清楚地说明发生了什么。我确信有更优雅的方法来实现所需的结果(我使用过 scalaz iteratees,但我根本不知道 Play API),但是至少一次通过显式解决方案工作很好,所以我们了解什么是真的在下面。

于 2015-01-03T23:09:06.053 回答