私はHaskellで小さなツールを作成した経験があり、特に標準入力を処理してそれを標準出力にパイプ処理するフィルター(interact
を使用)を作成する場合に、非常に直感的に使用できます。
最近、通常の約10倍のファイルにそのようなフィルターを1つ使用しようとすると、Stack space overflow
エラー。
いくつかの読み取りを行った後(例: here および here )スタックスペースを節約するための2つのガイドラインを確認しました(経験豊富なHaskellers、私が間違っているものを書いた場合は訂正してください):
seq
を導入して、部分式を早期に評価して、式が縮小される前に大きくなりすぎないようにします(これはHaskellに固有です。少なくとも遅延評価を使用する言語に固有です)。5つまたは6つのseq
呼び出しをコードに導入した後、ツールは再びスムーズに実行されます(より大きなデータに対しても)。ただし、元のコードはもう少し読みやすかったです。
私は経験豊富なHaskellプログラマではないので、この方法でseq
を導入することは一般的な方法であり、Haskellの製品コードでseq
が通常表示される頻度を尋ねたかったのです。または、seq
の使用を避け、スタック領域をほとんど使用しないテクニックはありますか?
残念ながら、大きなデータに対して効率的で適切に機能するプログラムを取得するためにseq
を使用する必要がある場合があります。したがって、多くの場合、量産コードではそれなしでは実行できません。詳しくは、Real World Haskellの 第25章「プロファイリングと最適化 」を参照してください。
ただし、seq
を直接使用しない方法もあります。これにより、コードがよりクリーンで堅牢になります。いくつかのアイデア:
interact
の代わりに conduit 、 pipes または iteratees を使用します。レイジーIOは、メモリだけでなく)リソースの管理に問題があることが知られており、反復処理はこれを克服するように設計されています(レイジーを回避することをお勧めしますIO =データがどれほど大きいかに関係なく-参照 遅延I/Oの問題 )seq
を使用する代わりに、 foldl ' または foldr' または厳密に設計されたライブラリの厳密なバージョン( Data.Map.Strict または Control.Monad.State.Strict など)計算。seq
を厳密なパターンマッチングで置き換えることができます。 strictコンストラクタフィールド を宣言することも、場合によっては役立ちます。rseq
)またはfullに強制するメソッドがあります [〜#〜] nf [〜#〜] (rdeepseq
)も同様です。コレクションの操作、戦略の組み合わせなどには、多くのユーティリティメソッドがあります。