web-dev-qa-db-ja.com

Haskellプロダクションコードでseqはどのくらいの頻度で使用されますか?

私はHaskellで小さなツールを作成した経験があり、特に標準入力を処理してそれを標準出力にパイプ処理するフィルター(interactを使用)を作成する場合に、非常に直感的に使用できます。

最近、通常の約10倍のファイルにそのようなフィルターを1つ使用しようとすると、Stack space overflowエラー。

いくつかの読み取りを行った後(例: here および here )スタックスペースを節約するための2つのガイドラインを確認しました(経験豊富なHaskellers、私が間違っているものを書いた場合は訂正してください):

  1. 末尾再帰ではない再帰的な関数呼び出しを避けます(これは末尾呼び出しの最適化をサポートするすべての関数型言語に有効です)。
  2. seqを導入して、部分式を早期に評価して、式が縮小される前に大きくなりすぎないようにします(これはHaskellに固有です。少なくとも遅延評価を使用する言語に固有です)。

5つまたは6つのseq呼び出しをコードに導入した後、ツールは再びスムーズに実行されます(より大きなデータに対しても)。ただし、元のコードはもう少し読みやすかったです。

私は経験豊富なHaskellプログラマではないので、この方法でseqを導入することは一般的な方法であり、Haskellの製品コードでseqが通常表示される頻度を尋ねたかったのです。または、seqの使用を避け、スタック領域をほとんど使用しないテクニックはありますか?

23
Giorgio

残念ながら、大きなデータに対して効率的で適切に機能するプログラムを取得するためにseqを使用する必要がある場合があります。したがって、多くの場合、量産コードではそれなしでは実行できません。詳しくは、Real World Haskellの 第25章「プロファイリングと最適化 」を参照してください。

ただし、seqを直接使用しない方法もあります。これにより、コードがよりクリーンで堅牢になります。いくつかのアイデア:

  1. interactの代わりに conduitpipes または iteratees を使用します。レイジーIOは、メモリだけでなく)リソースの管理に問題があることが知られており、反復処理はこれを克服するように設計されています(レイジーを回避することをお勧めしますIO =データがどれほど大きいかに関係なく-参照 遅延I/Oの問題
  2. seqを使用する代わりに、 foldl ' または foldr' または厳密に設計されたライブラリの厳密なバージョン( Data.Map.Strict または Control.Monad.State.Strict など)計算。
  3. BangPatterns 拡張を使用します。 seqを厳密なパターンマッチングで置き換えることができます。 strictコンストラクタフィールド を宣言することも、場合によっては役立ちます。
  4. Strategies を使用して評価を強制することもできます。戦略ライブラリは主に並列計算を目的としていますが、値を [〜#〜] whnf [〜#〜]rseq)またはfullに強制するメソッドがあります [〜#〜] nf [〜#〜]rdeepseq)も同様です。コレクションの操作、戦略の組み合わせなどには、多くのユーティリティメソッドがあります。
17
Petr Pudlák