GHC は関数をメモ化しません。
ただし、コード内の特定の式は、周囲のラムダ式が入力されるたびに最大 1 回、最上位にある場合は最大 1 回計算されます。例のようにシンタックス シュガーを使用する場合、ラムダ式がどこにあるかを判断するのは少し難しい場合があるため、これらを同等の desugared 構文に変換しましょう。
m1' = (!!) (filter odd [1..]) -- NB: See below!
m2' = \n -> (!!) (filter odd [1..]) n
(注: Haskell 98 のレポートでは、実際には のような左演算子セクションを(a %)
と同等と記述しています\b -> (%) a b
が、GHC はそれを に脱糖し(%) a
ます。これらは で区別できるため、技術的には異なりますseq
。これについて GHC Trac チケットを提出した可能性があると思います。)
これを考えるとm1'
、 では式filter odd [1..]
がどのラムダ式にも含まれていないことがわかります。したがって、プログラムの実行ごとに 1 回だけ計算されますが、m2'
ではfilter odd [1..]
ラムダ式が入力されるたびに計算されます。つまり、の呼び出しごとにm2'
。それはあなたが見ているタイミングの違いを説明しています。
実際、特定の最適化オプションを備えた GHC のいくつかのバージョンは、上記の説明が示すよりも多くの値を共有します。これは、状況によっては問題になる可能性があります。たとえば、関数を考えてみましょう
f = \x -> let y = [1..30000000] in foldl' (+) 0 (y ++ [x])
y
GHCは関数に依存していないことに気づきx
、関数を
f = let y = [1..30000000] in \x -> foldl' (+) 0 (y ++ [x])
この場合、新しいバージョンは が格納されているメモリから約 1 GB を読み取る必要があるため、効率が大幅に低下しますがy
、元のバージョンは一定のスペースで実行され、プロセッサのキャッシュに収まります。実際、GHC 6.12.1 では、この関数は、最適化なしf
でコンパイルした場合に、 を使用してコンパイルした場合よりもほぼ 2 倍速くなります。-O2