2011-11-26 4 views
5

もしそうなら、これは標準の一部ですか、あるいは私たちが依存できるghc固有の最適化ですか?あるいは、私たちが必ずしも依存することのできない最適化だけです。効率性の理由から、ghcはリストを一度だけジェネレータに変換しますか?

P.S: 私は試験サンプルを試したとき、行われていたことを示しているように見えた/

Prelude> let isOdd x = x `mod` 2 == 1 
Prelude> let isEven x = x `mod` 2 == 0 
Prelude> ((filter isOdd).(filter isEven)) [1..] 
CPUアップ

咀嚼が、多くのメモリを消費しません。

+2

インタープリタを使用して最適化を確認していますか? – delnan

+1

良い点は、それをコンパイルした、同じことが起こる –

+4

'フィルタは奇妙です。 $ [1 ..] even even filter。 – is7s

答えて

7

発電機の意味によって異なります。リストは遅れて生成され、それを参照するものは何もないため、消費された部分はほとんど直ちにガベージコレクションされます。上記の計算の結果は増加しないので、計算全体が一定の空間で実行されます。これは標準によって義務付けられているわけではありませんが、その例(および曖昧に似ている)のために異なる空間動作を持つ厳密でないセマンティクスを実装することは難しいので、実際にはそれに頼ることができます。

通常、リストはまだリストとして生成されているため、多くのゴミが生成されています。有利な状況下で、GHCはリスト[1 .. ]を排除し、非割り当てループ生成する:

result :: [Int] 
result = filter odd . filter even $ [1 .. ] 

(怠惰のうちプレリュード関数を使用して)、-O2でコンパイルは、コア

List.result_go = 
    \ (x_ayH :: GHC.Prim.Int#) -> 
    case GHC.Prim.remInt# x_ayH 2 of _ { 
     __DEFAULT -> 
     case x_ayH of wild_Xa { 
      __DEFAULT -> List.result_go (GHC.Prim.+# wild_Xa 1); 
      9223372036854775807 -> GHC.Types.[] @ GHC.Types.Int 
     }; 
     0 -> 
     case x_ayH of wild_Xa { 
      __DEFAULT -> List.result_go (GHC.Prim.+# wild_Xa 1); 
      9223372036854775807 -> GHC.Types.[] @ GHC.Types.Int 
     } 
    } 

普通ループを生成します1からmaxBound :: Intまで実行され、途中で何も生成せず、最後に[]を生成します。 []を明白に返すほどスマートです。 GHCは、Intが偶数である場合、奇数にすることはできないので、チェックは削除され、ブランチには空でないリストが作成されます(つまり、到達不能な枝はコンパイラによって削除されている)。

+0

もし 'filter(\ x - > odd x && even x)[1. ..]'ならば、何とかラムダを効果的に 'const False'に変えるほどスマートでしょうか? – MatrixFrog

+1

@MatrixFrogいいえ、まだありません。 '\ x - > seq x False'(1)でなければならないので、'(\ x - > odd x && even x) 'を 'const False'にすることはできません。この場合、 '' Int 'と他の既知の型のコンパイラは '[1 ..]'に底がないことを知っていますので、そうすることができますが、そのような状況がしばしば起こり、そのような分析が価値があるとは思わない)。 (1)いくつかの既知のタイプの場合。一般に、残りのmod 2は計算され、0と比較されなければならない。なぜなら、それらの計算は非終端である可能性があるからである。 –

+0

@MatrixFrog実際には、完全に合法的なインスタンスが存在しますが、その最適化は_ボトムを無視しても_ワールド_になります。例えば、 'simple-reflect'パッケージによって提供される' Expr'型は、このような 'Num'と' Integral'インスタンスの多くの "法則"を破ります。 –

2

厳密に言えば、Haskellは特定の評価モデルを指定していないため、実装は自由に言語の意味を実装することができます。しかし、GHCをはじめとする元気な実装では、これを一定の空間で実行することに頼ることができます。

GHCでは、これらのような計算は、まだ評価されていない残りのリストを表すサンクで終わる単独リンクの結果となります。このリストを評価すると、リストの多くはオンデマンドで生成されますが、リストの最初は他の場所では参照されないため、直前の部分はすぐにガベージコレクションの対象となります。

最適化が有効になっている場合、GHCはここで森林破壊を実行し、リストを作成する必要性を最適化し、割り当てが行われない単純なループになります。

+2

割り当てのないループは、一部のタイプでのみ使用できます。 'Integer'では、型シグネチャがなくても、 'loop counter'はヒープ割り当ての' Integer'でなければなりません。主な点は、リストの削除、立つ。 –

+0

あなたが言及した森林破壊プロセスの詳細はどこで読むことができますか? – haskelline

+0

@brence:リスト上のGHCで現在使用されている森林破壊技術は、[foldr/build-fusion](http://www.haskell。org/haskellwiki/Correctness_of_short_cut_fusion#foldr.2Fbuild_4)、[stream fusion](http://stackoverflow.com/questions/578063/what-is-haskells-stream-fusion)と呼ばれる別のフォームは 'vector'パッケージによって使用されます。 HaskellWikiには、さまざまな森林破壊技術に関する広範なリスト(http://www.haskell.org/haskellwiki/Research_papers/Compilation#Fusion_and_deforestation)もあります。それらは始めるには良い場所でなければなりません。 – hammar

関連する問題