从有状态算法创建一个枚举器 [英] Creating an Enumeratee from a stateful algorithm

查看:115
本文介绍了从有状态算法创建一个枚举器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我试图将它变成 Enumeratee ,但是我很难入门。



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

  var state ='foo 
var i = input()
while(i!= null){
if(state =='foo&& i> = 0 ){
// 2个输出,并且改变状态
输出(i-2)
输出(i * 3)
state ='bar
} else if( state =='foo){
//错误
错误(预计> = 0)
} else if(state =='bar){
// 0输出和改变状态
state ='foo
}
... // etc
i = input()
}
if(state!= '酒吧) {
error(unexpected end)
}

我研究过 map ,过滤器等实现playframework / blob / 2.3.x / framework / src / iteratees / src / main / scala / play / api / libs / iteratee / Enumeratee.scalarel =nofollow> Enumeratee.scala ,我有点理解他们。但是我很难看到如何编写自己的更复杂的实现。

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

解决方案

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

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

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

pre code> def next [A](inner:Iteratee [To,A],i:输入[来自],状态:符号)
: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 _ =>
//通过Empty或Eof
Iteratee.flatten(inner.feed(i))
}
def applyOn [A](inner:Iteratee [To,A]) :Iteratee [From,Iteratee [To,A]] =
Cont {i =>下一个(内部,我,'富)}

注意我们如何返回直接递归调用 next ,或者是一个将最终进行(相互)递归调用到 next的继续。



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


I have a stateful algorithm that incrementally takes input and incrementally produces output. The inputs and outputs are unrelated in number; i.e. an input may produce zero or more outputs.

I am attempting to turn it into an Enumeratee in the Play Framework, but I am having difficulty getting started.

My algorithm has local mutable state and synchronous operations and looks something like this

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")
}

I've studied the map, filter, etc. implementations in Enumeratee.scala, and I sort of understand them. But I'm having trouble seeing how to write my own implementation of something more complicated.

Could you describe/demonstrate how I can transform this algorithm into an Enumeratee?

解决方案

The only thing you need to implement is the applyOn method:

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

Everything else is implemented in the trait.

When creating an iteratee, I find recursion is the most important trick; it's a continuation-like style where rather than returning anything, each step computes the thing it needs to compute and then calls into it again. So your state should become a function parameter:

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)}

Notice how we return either a direct recursive call to next, or a continuation that will eventually make a (mutually) recursive call to next.

I've passed the Input around explicitly to every call, whereas a more elegant approach might handle it in the continuation (and perhaps implement applyOn directly rather than having it as a wrapper method), but hopefully this makes it clear what's going on. I'm sure there are more elegant ways to achieve the desired result (I've used scalaz iteratees but I don't know the Play API at all), but it's nice to work through the explicit solution at least once so we understand what's really going on underneath.

这篇关于从有状态算法创建一个枚举器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆