3

ツールボックスに Haskell を追加したいので、Real World Haskellを使って作業しています。

入力と出力の章の のセクションでhGetContents、次の例に出くわしました。

import System.IO
import Data.Char(toUpper)

main :: IO ()
main = do 
    inh <- openFile "input.txt" ReadMode
    outh <- openFile "output.txt" WriteMode
    inpStr <- hGetContents inh
    let result = processData inpStr
    hPutStr outh result
    hClose inh
    hClose outh

processData :: String -> String
processData = map toUpper

このコード サンプルに続いて、著者は次のように続けます。

hGetContentsがすべての読み取りを処理したことに注意してください。もご覧くださいprocessData。副作用がなく、呼び出されるたびに常に同じ結果を返すため、これは純粋な関数です。この場合、入力がファイルから遅延して読み取られていることを知る必要はなく、知る方法もありません。20 文字のリテラルまたはディスク上の 500 GB のデータ ダンプで完全に機能します。 (注:強調は私のものです)

私の質問はhGetContents、この例では「伝えることができない」ことなく、またはその結果の値がこのメモリ効率をどのように達成し、純粋なコード (つまり、メモ化) に生じるprocessDataすべての利点を維持するのですか?processData

<- hGetContents inhは文字列を返すためinpStr、 type の値にバインドされます。これは、受け入れるString型とまったく同じです。processDataしかし、Real World Haskell の作成者を正しく理解していれば、この文字列はメモリに完全にロードされていない (または完全に評価されていない文字列が存在する場合は完全に評価されていない) という点で、他の文字列とはまったく異なります。 .) への呼び出し時までにprocessData

したがって、私の質問をする別の方法は次のとおりです。inpStrへの呼び出し時に が完全に評価されていないか、メモリにロードされていない場合、最初に を完全に評価せずに、processDataへのメモ化された呼び出しが存在するかどうかを調べるためにどのように使用できますか?processDatainpStr

Stringそれぞれが異なる動作をするが、このレベルの抽象化では区別できないタイプのインスタンスはありますか?

4

1 に答える 1

4

Stringby が返す値は、他のhGetContentsHaskell 文字列と変わりません。一般に、Haskell データは、プログラマーがそれを確実にするための特別な手順を実行しない限り、完全には評価されません (例: seqbangdeepseqパターン)。

文字列は(本質的に)次のように定義されます

data List a = Nil | Cons a (List a) -- Nil === [], Cons === :
type String = List Char

これは、文字列が空であるか、1 文字 (先頭) と別の文字列 (末尾) であることを意味します。怠惰のために、テールはメモリに存在しない可能性があり、無限でさえある可能性があります。を処理するStringと、Haskell プログラムは通常、それがNilまたはであるかどうかをチェックConsし、必要に応じて分岐して処理を進めます。関数が末尾を評価する必要がない場合は、評価しません。たとえば、この関数は初期コンストラクターのみをチェックする必要があります。

safeHead :: String -> Maybe Char
safeHead [] = Nothing
safeHead (x:_) = Just x

これは完全に正当な文字列です

allA's = repeat 'a' :: String

それは無限です。この文字列は適切に操作できますが、すべてを印刷しようとしたり、長さを計算しようとしたり、あらゆる種類の制限のないトラバーサルを実行しようとすると、プログラムは終了しません。しかし、関数 likesafeHeadをまったく問題なく使用でき、有限の初期部分文字列を消費することさえできます。

しかし、何か奇妙なことが起こっているというあなたの直感は正しいです。 hGetContents特別な関数unsafeInterleaveIOを使用して実装されます。これは本質的に動作へのコンパイラ フックIOです。これについてはあまり言わないほうがいいです。

引数を完全に評価せずに関数へのメモ化された呼び出しが存在するかどうかを確認するのは難しいでしょう。ただし、ほとんどのコンパイラはこの最適化を実行しません。問題は、呼び出しをメモ化する価値があるかどうかをコンパイラが判断するのが非常に難しく、そうすることですべてのメモリを簡単に消費してしまうことです。幸いなことに、必要に応じてメモ化を追加するために使用できるメモ化ライブラリがいくつかあります。

于 2013-10-17T07:41:27.930 に答える