Haskell製品コードでseqはどのくらいの頻度で使用されますか?


23

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

最近、通常の約10倍のサイズのファイルでこのようなフィルターを使用しようとしましたが、Stack space overflowエラーが発生しました。

いくつか読んだ後(ここここなど)、スタックスペースを節約するための2つのガイドラインを特定しました(経験のあるHaskellers、正しくないものを書いたら修正してください):

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

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

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


1
あなたが説明したような最適化は、ほとんど常にコードを少しエレガントにしません。
ロバートハーベイ

@Robert Harvey:スタック使用量を低く抑えるための代替技術はありますか?私は自分の関数を別の方法で書き直さなければならないことを想像していますが、確立されたテクニックがあるかどうかはわかりません。私の最初の試みは、末尾再帰関数を使用することでしたが、これは問題を完全には解決できませんでした。
ジョルジオ

回答:


17

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

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

  1. の代わりにコンジットパイプ、または反復子を使用しinteractます。レイジーIOには(メモリだけでなく)リソースの管理に問題があることが知られており、反復子はこれを克服するように設計されています。(データがどれほど大きくても、レイジーIOをすべて回避することをお勧めします。レイジーI / Oの問題を参照してください。)
  2. foldl 'foldr'seqなどのコンビネータを直接使用(または独自に設計)する代わりに、厳密な計算用に設計されたライブラリの厳密なバージョン(Data.Map.StrictControl.Monad.State.Strictなど)を使用します。
  3. BangPatterns拡張機能を使用します。seq厳密なパターンマッチングに置き換えることができます。場合によっては、厳密なコンストラクタフィールドを宣言することも役立ちます。
  4. 評価を強制するための戦略を使用することもできます。ストラテジーライブラリは、主に並列計算を目的としていますが、値をWHNFrseq)または完全NFrdeepseq)に強制する方法もあります。コレクションの操作、戦略の組み合わせなどには、多くのユーティリティメソッドがあります。

+1:有用なヒントとリンクをありがとう。ポイント3は非常に興味深いようです(そして、私にとって今最も簡単な解決策です)。提案1に関しては、遅延IOを回避することが物事をどのように改善できるかわかりません:遅延IOがデータのストリーム(おそらく非常に長い)を処理することになっているフィルターにとってより良いはずだと理解している限り。
ジョルジオ

2
@Giorgio私は、Lazy IOの問題に関するHaskell Wikiへのリンクを追加しました。遅延IOを使用すると、リソースの管理が非常に困難になります。たとえば、入力を完全に読み取れない場合(遅延評価など)、ファイルハンドルは開いままになります。また、ファイルハンドルを手動で閉じてしまうと、評価の遅延評価のためにファイルの読み取りが延期され、入力全体を読み取る前にハンドルを閉じることがよくあります。また、レイジーIOでメモリの問題を回避することは非常に困難です。
ペトルプドラク

最近この問題が発生し、プログラムでファイル記述子が不足していました。だから私はstrictを使用して遅延IOをstrict IOに置き換えましたByteString
ジョルジョ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.