从有状态算法创建一个枚举器 [英] Creating an Enumeratee from a stateful algorithm
问题描述
我有一个有状态的算法,逐渐地接受输入并逐渐产生输出。投入和产出在数量上无关;即一个输入可能会产生零个或多个输出。
我试图将它变成 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屋!