前の質問と同様に、Data.Binary.Putモナドを別のモナドにラップして、後で「何バイトを書き込むか」や「ファイル内の現在の位置は何か」などの質問をすることができるようにしようとしています。 。
以前は、些細な(IdentityT?)ラッパーを使用しているときにメモリリークが発生する理由を理解することで、問題を解決できると思いました。しかし、皆さんが些細なラッパーの問題を解決するのを手伝ってくれたとしても、StateTやWriterTのような便利なものでラップすると、メモリを大量に消費します(通常はクラッシュします)。
たとえば、これは私がそれをラップしようとしている1つの方法であり、大きな入力のためにメモリをリークします。
タイプOut=StateT Integer P.PutM() writeToFile::文字列->出力->IO() writeToFile path out = BL.writeFile path $ P.runPut $ do runStateT out 0 戻る ()
これは、問題を示すより完全なコードサンプルです。
私が知りたいのはこれです:
- メモリリークの原因となるプログラム内で何が起こっていますか?
- それを修正するにはどうすればよいですか?
2番目の質問では、データがディスク上でどのように見えるかをより詳細に説明する必要があると思います。これは基本的に、ツリーの各ノードがその子(およびいくつかの追加データ)へのオフセットテーブルとして表されるツリー構造です。したがって、オフセットテーブルへのn番目の子のオフセットを計算するには、0からn-1までの子のサイズに現在のオフセットを加えたものを知る必要があります(簡単にするために、各ノードに固定数の子があるとします)。
見てくれてありがとう。
更新:nominoloのおかげで、Data.Binary.Putをラップし、現在のオフセットを追跡し、メモリをほとんど使用しないモナドを作成できるようになりました。これは、継続を使用する別の状態のスレッドメカニズムを優先して、StateTトランスフォーマーの使用をやめることによって行われます。
このような:
タイプOffset=Int newtype MyPut a = MyPut {unS ::forallr。(オフセット-> a-> P.PutM r)->オフセット-> P.PutM r} インスタンスモナドMyPutここで a = MyPut $ \fs->fsaを返します ma >> = f = MyPut $ \ fb s-> unS ma(\ s'a-> unS(fa)fb s')s writeToFile :: String-> MyPut()-> IO() writeToFileパスput= BL.writeFileパス$P.runPut$ peal put >> return() ここで、peal myput = unS myput(\ o-> return)0 getCurrentOffset :: MyPut Int getCurrentOffset = MyPut $ \ fo-> foo lift'n ma = MyPut $ \ fs-> ma >> = f(s + n)
ただし、MyPutがディスクに書き込むバイト数の追跡にはまだ問題があります。特に、次のような署名付きの関数が必要です。
getSize :: MyPut a-> MyPut Intまた
getSize :: MyPut a-> Int
私のアプローチは、MyPutモナドをWriterTトランスフォーマー(このようなもの)内にラップすることでした。しかし、それは再びあまりにも多くのメモリを消費し始めました。sclvがnominolosの回答の下のコメントで言及しているように、WriterTは継続の効果をどういうわけかキャンセルします。彼はまた、私がすでに持っているMyPutモナドから直接サイズを取得できるはずだと述べていますが、そうしようとするすべての試みは、コンパイルできないコードまたは無限ループで終わりました:-|。
誰かがさらに助けてくれませんか?