2015-01-03 66 views
2

我有一個有狀態的算法,它逐步獲取輸入並逐漸產生輸出。投入和產出在數量上無關;即一個輸入可能產生零個或多個輸出。從有狀態算法創建一個枚舉器

我試圖在Play框架中將它變成Enumeratee,但我很難入門。

我的算法具有本地可變狀態和同步操作,看起來像這樣

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

我研究了mapfilter等實現在Enumeratee.scala,和我有點理解他們。但是我很難看到如何編寫我自己的更復雜的實現。

您能否描述/演示如何將此算法轉換爲Enumeratee

回答

2

需要實現的唯一的事情就是applyOn方法:

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

其他的一切在他們的特性實現。

當創建迭代器時,我發現遞歸是最重要的技巧;它是一種類似於延續的風格,它不是返回任何東西,而是每個步驟計算需要計算的東西,然後再次調用它。所以,你的狀態應該成爲一個功能參數:

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),但是至少有一次通過顯式解決方案很好,所以我們理解了什麼是真正在下面。

+0

嗯......我差點把它......'Iteratee#feed'和'Iteratee.flatten'都丟失了。謝謝。我也懷疑是否有一些Play圖書館功能的「更好」形式,但這當然很容易遵循,並幫助我理解發生了什麼。 –