2016-02-22 5 views
9

副作用の「プロデューサ」関数f:() => Option[T]があり、それはいつまでもNoneを返す将来のポイントまで繰り返し呼び出すとSomeを返します。 (例えば、EOFでnullを生成するラップされたJava APIは、このような振る舞いを持つ可能性があります)。Optionを返す副作用関数をIteratorに戻す

は、次の制約で、TraversableOnceまたはIteratorのようなものには、この機能をラップすることが可能です:

  • 標準のScalaライブラリー構築物は、シーケンスは任意の長さかもしれ
  • を優先し、すべての値を保持します、例えばStream
  • は同様
  • 可視ユーザーのソースコードが

は、いくつかの有用な方法がありvar

  • スレッドの安全性が必要とされない使用しないでくださいスタックオーバーフローの可能性があってはならない望まれませんIteratorオブジェクトでは、私の使用例と正確には一致しません。任意のアイデアを歓迎!

  • 答えて

    6

    これは、トリックを行います。

    def wrap[T](f:() => Option[T]): Iterator[T] = { 
        Iterator.continually(f()).takeWhile(_.isDefined).flatten  
    } 
    

    REPL試験:

    scala> :paste 
    // Entering paste mode (ctrl-D to finish) 
    var i = 0 
    def sideEffectingFunc(): Option[Int] = { 
        i += 1 
        if (i < 10) Some(i) 
        else None 
    } 
    // Exiting paste mode, now interpreting. 
    
    i: Int = 0 
    sideEffectingFunc:()Option[Int] 
    
    scala> val it = wrap(sideEffectingFunc) 
    it: Iterator[Int] = non-empty iterator 
    
    scala> it.toList 
    res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) 
    
    +0

    優れた迅速な回答をいただきありがとうございます。 – satyagraha

    +0

    最後のバージョンの.map(_。get)は、Scalaの最近のバージョンでは暗黙のTraversableOnce CBFとそれに関連するイテレータを導入する '.flatten'で置き換えられます。私はこれがまだ私の基準を満たしていると思います。 – satyagraha

    +0

    あなたはそうです。私はそれを逃しました。なぜなら、Iteratorのscaladocには言及していないからです。 scaladocバグのようです: 'flatten'はエンリケメントクラス' TraversableOnce.FlattenOps'から来て、enrichementsはscaladoc(そして多くの人が扱うことになっています。私は自分の答えを更新しました。 –

    2

    やや直交、この現象がコルーチンを使用して達成することができます。コルーチンを可能にScalaのための少なくとも一つのライブラリがあり、あなたはここでそれを見つけることができます:代わりに、

    import org.coroutines._ 
    
    def sideEffectingFunction = coroutine {() => 
        val limit = new scala.util.Random().nextInt(10) 
        val seq = new scala.util.Random 
        var counter = 0 // mutable state is preserved between coroutine invocations 
        while (counter < limit) { 
        counter += 1 
        yieldval(seq.nextInt) 
        } 
    } 
    defined function sideEffectingFunction 
    
    @ val cr = call(sideEffectingFunction()) 
    cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true> 
    @ cr.resume 
    res31: Boolean = true 
    @ cr.value 
    res32: Int = 57369026 
    @ cr.resume 
    res33: Boolean = true 
    @ cr.value 
    res34: Int = -1226825365 
    @ cr.resume 
    res35: Boolean = true 
    @ cr.value 
    res36: Int = 1304491970 
    @ cr.resume 
    res37: Boolean = false 
    @ cr.value 
    java.lang.RuntimeException: Coroutine has no value, because it did not yield. 
        scala.sys.package$.error(package.scala:27) 
        org.coroutines.Coroutine$Frame$mcI$sp.value$mcI$sp(Coroutine.scala:130) 
        cmd38$.<init>(Main.scala:196) 
        cmd38$.<clinit>(Main.scala:-1) 
    

    をまたは:ここ

    http://storm-enroute.com/coroutines/はあなたが望む結果を得るために書くつもりコードの例です:

    @ val cr = call(sideEffectingFunction()) 
    cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true> 
    @ cr.resume 
    res60: Boolean = true 
    @ val iter = Iterator.continually(cr.value).takeWhile(_ => cr.resume) 
    iter: Iterator[Int] = non-empty iterator 
    @ iter.foreach(println) 
    1595200585 
    995591197 
    -433181225 
    220387254 
    201795229 
    754208294 
    -363238006 
    

    T:前の回答の精神で

    @ val cr = call(sideEffectingFunction()) 
    cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true> 
    @ while(cr.resume) println(cr.value) 
    -1888916682 
    1135466162 
    243385373 
    

    あるいは、コルーチンのアプローチの利点は、根底にある副作用関数の呼び出しの間に変更可能な状態を保つことができることです。すべてがコルーチン内の外側の世界からうまく隠されています。コルーチンは、構成してお互いに呼び出すこともできます。

    もちろん、コルーチンは仕事を遂行するよりもはるかに強力なパワーを提供します。だから、これはちょうどこれを追加するのは難しいかもしれません。しかし、それは知っておくと便利なテクニックです。

    関連する問題