2013-03-06 8 views
13

コンジットパイプの違いを理解しようとしています。 パイプとは異なり、コンジットには、残り物という概念があります。残されたものは何か?私は残り物が欠かせないいくつかの例を見たいと思います。コンジットの残り物のメリットは何ですか?

パイプには残り物の概念がないので、同様の動作を達成する方法はありますか?

答えて

17

残酷な部分が常にパーズの一部であるというガブリエルの点は面白いです。私は同意するかどうかはわかりませんが、それは構文解析の定義に依存している可能性があります。

残存を必要とする大きなカテゴリのユースケースがあります。構文解析は確かに1つです。解析に何らかの先取りが必要なときは、残り物が必要になります。これの1つの例は、markdownパッケージのgetIndented関数です。これは、次の行をすべて特定のインデントレベルで分離し、残りの行を後で処理するようにします。

しかし、もっと世俗的な例がコンジット自体に存在します。パックされたデータ(ByteStringやTextなど)を扱うときはいつも、チャンクを読み、それを何らかの形で分析し、余分に使用して余分を押し戻し、元のコンテンツで何かを行う必要があります。おそらく最も簡単な例はdropWhileです。

実際、残ったものは、コンジットのための新しい1.0インターフェイスが残量を無効にするユーザーにオプションを公開することさえない、ストリーミングライブラリの核となる基本的な機能であると考えています。私は、それを必要としない現実世界のユースケースがごくわずかしかないことを知っています。

+0

説明をいただきありがとうございます。私は今確信しています。一方、私はネイティブに持っていない導管のような図書館の上に残り物を実装する方法について考えていました。残りの部分を持つコンジットは '(Maybe i、r)'を返すコンジットとして表すことができます。私の試み(コンジット用)は[こちら](https://gist.github.com/ppetr/5110909)です。 –

+0

私はあなたが正しい直感を持っていると思います。あなたの実装は内部の仕組みと非常によく似ています。私はあなたが残りの2つの問題を発見したと思います。これは残り物がコンジットの中の複数のLeftoverコンストラクタとして積み重なることが許される理由です。 –

+0

残量のための[別の試み](https://gist.github.com/ppetr/5110909#file-feedback-hs)(残念ながら私のScalaライブラリで使用する可能性が高い)は、残り物を一種のものとして見ることですフィードバック: 'パイプボイドi(いずれかのIO)umr'では、このようなパイプを標準のパイプに変換する内部メソッドを使用して、入力に' Left i'を送ります。 –

15

pipesについてはお答えいたします。あなたの質問に対する短い答えは、来るべきpipes-parseライブラリがより一般的な解析フレームワークの一部として残り物をサポートするということです。私は、人々が残り物を実際にパーサーがほしいと思うほぼすべてのケースがあることを発見します。それは、残りの問題を解析のサブセットとしてフレーム化する理由です。ライブラリhereの現在のドラフトを見つけることができます。

しかし、pipes-parseが機能する方法を理解したい場合は、残りの部分を実装する最も簡単な方法は、StatePを使用してプッシュバックバッファを保存することです。これが唯一の次の2つの関数を定義する必要があります。

import Control.Proxy 
import Control.Proxy.Trans.State 

draw :: (Monad m, Proxy p) => StateP [a] p() a b' b m a 
draw = do 
    s <- get 
    case s of 
     [] -> request() 
     a:as -> do 
      put as 
      return a 

unDraw :: (Monad m, Proxy p) => a -> StateP [a] p() a b' b m() 
unDraw a = do 
    as <- get 
    put (a:as) 

draw最初の利用可能な場合スタックから一つの要素をポップ、任意の格納された要素があるかどうかを確認するためにプッシュバックバッファを参照。バッファが空の場合、代わりにアップストリームから新しい要素を要求します。もちろん、何かを戻すことができない場合、バッファを持つポイントはありません。したがって、後で保存するために要素をスタックにプッシュするようにも定義します(unDraw)。

編集:残念ながら、余剰が役に立つ場合の有用な例を含めるのを忘れました。マイケルが言っているように、takeWhiledropWhileは残余の有用なケースです。ここdrawWhile関数は(マイケルがtakeWhile呼んでいるものに類似)です:

producer() = do 
    respond 1 
    respond 3 
    respond 4 
    respond 6 

...そしてあなたが使用する消費者にそれをフックアップ:

drawWhile :: (Monad m, Proxy p) => (a -> Bool) -> StateP [a] p() a b' b m [a] 
drawWhile pred = go 
    where 
    go = do 
     a <- draw 
     if pred a 
     then do 
      as <- go 
      return (a:as) 
     else do 
      unDraw a 
      return [] 

今、あなたのプロデューサーであったことが想像

consumer() = do 
    evens <- drawWhile odd 
    odds <- drawWhile even 

最初のdrawWhile oddが最後の要素をプッシュバックしなかった場合は、4を削除します正しく第2のdrawWhile evenステートメントに渡されます。

関連する問題