遅延評価のマイナス面が気になるようですね。折り畳み、ループ、再帰が定数メモリで処理されるようにする必要があります。
iteratee ライブラリは、この問題を
解決
する
ために作成
されました
。
最も人気があり、最近のものは
パイプと
コンジットです。どちらも iteratee モデルを超えています。
パイプライブラリは、
バグを排除し、設計の一貫性が効率的でありながら高いレベルの抽象化を可能にするために、理論的に健全であることを重視しています (私の言葉は著者ではありません)。また、必要に応じて双方向ストリームも提供します。これは、これまでライブラリに固有の利点です。
コンジットは、
パイプほど理論的には確立されていませんが、現在、http ストリーム、xml ストリームなどを解析および処理するために、より多くの関連ライブラリが構築されているという大きな利点があります。パッケージページのハックインでコンジットセクションをチェックしてください。Haskellの大規模でよく知られている Web フレームワークの 1 つとして使用され
ています。
パイプ ライブラリ、特にプロキシ トランスフォーマー スタックを作成する機能を使用して、ストリーミング アプリケーションを作成することを楽しんでいます。Web ページを取得したり、いくつかの xml を解析したりする必要があるときは、コンジット ライブラリを使用しています。
また、最初の公式リリースを行ったばかりの io-streamsについても言及する必要があり
ます。その目的は特に IO であり、その名前にあるのは当然のことであり、より単純な型の機械、より少ない型パラメーター、そして
パイプまたは
コンジットを利用しています。主なマイナス面は、IO モナドで立ち往生しているため、純粋なコードにはあまり役に立たないことです。
{-# language NoMonoMorphismRestriction #-}
import Control.Proxy
簡単な翻訳から始めます。
map (+1) [1..10]
になります:
runProxy $ mapD (+1) <-< fromListS [1..10]
iteratee like は、単純な翻訳のためにもう少し冗長な提供を提供しますが、より大きな例で大きな利益をもたらします。
定数空間でフィボナッチ数を生成するプロキシ、パイプ ライブラリの例
fibsP = runIdentityK $ (\a -> do respond 1
respond 1
go 1 1)
where
go fm2 fm1 = do -- fm2, fm1 represents fib(n-2) and fib(n-1)
let fn = fm2 + fm1
respond fn -- sends fn downstream
go fm1 fn
これらは、runProxy $ fibsP >-> printD を使用して stdout にストリーミングできます -- printD はダウンストリームの値のみを出力します。プロキシはパイプ パッケージの双方向のオファーです。
プロキシ チュートリアルとコンジット チュートリアルをチェックしてください。私が見つけたばかりの FP Complete の Haskell の学校にあります。
平均を見つける 1 つの方法は次のとおりです。
> ((_,l),s) <- (`runStateT` 0) $ (`runStateT` 0) $ runProxy $ foldlD' ( flip $ const (+1)) <-< raiseK (foldlD' (+)) <-< fromListS [1..10::Int]
> let m = (fromIntegral . getSum) s / (fromIntegral . getSum) l
5.5
これで、マップの追加やプロキシのフィルタリングが簡単になりました。
> ((_,l),s) <- (`runStateT` 0) $ (`runStateT` 0) $ runProxy $ foldlD' ( flip $ const (+1)) <-< raiseK (foldlD' (+)) <-< filterD even <-< fromListS [1..10::Int]
編集: 状態モナドを利用するために書き直されたコード。
アップデート:
コンパス可能な方法で大量のデータ ストリームに対して複数の計算を実行し、直接再帰を記述する方法については、ブログ記事の美しい折りたたみで説明されています。フォールドはデータに変換され、厳密なアキュムレータを使用して結合されます。私はこの方法を定期的に使用したことはありませんが、厳密さが必要な場所を分離して適用しやすくしているようです。また、Applicative を使用して同じメソッドを実装し、好みによっては読みやすい別の質問の同様の質問への回答も参照する必要があります。