最近F#ソースコードを掘り下げています。 Seq.fsでF#Seqの実装上の問題
:最初のものは非常に高速である
let rec rwalk n = seq { if n > 0 then
yield n
yield! rwalk (n-1)
}
と
let rec rwalk n = seq { if n > 0 then
yield! rwalk (n-1)
yield n
}
は、私が見つけた:
// Binding.
//
// We use a type defintion to apply a local dynamic optimization.
// We automatically right-associate binding, i.e. push the continuations to the right.
// That is, bindG (bindG G1 cont1) cont2 --> bindG G1 (cont1 o cont2)
// This makes constructs such as the following linear rather than quadratic:
//
// let rec rwalk n = { if n > 0 then
// yield! rwalk (n-1)
// yield n }
上記のコードを見た後、私は2つのコードをテスト、2番目は非常に遅いです。 n = 10000の場合、このシーケンスを生成するために私のマシンでは3秒かかります。つまり2次の時間です。
二次的な時間は合理的です。
seq { yield! {1; 2; ...; n-1}; yield n }
は私が推測する、線形時間を取る必要があります
Seq.append {1; 2; ...; n-1} {n}
この追加操作に変換されます。最初のコードでは、追加操作は次のようになります:seq { yield n; yield! {n-1; n-2; ...; 1} }
、一定の時間がかかります。
コード内のコメントは、それがlinear
(多分この線形は線形時間ではない)であると言う。おそらく、linear
は、Moand/F#計算式ではなく、シーケンスのカスタマイズされた実装を使用することに関連しています(F#仕様で述べられていますが、その理由は言及していません...)。
ここではあいまいさを明確にすることはできますか?どうもありがとう!
(これは、言語の設計と最適化問題であるPSので、私もそこに人々が洞察力を持っているかどうかを確認するためにHaskellのタグを添付。)
+1ソースコードを掘るために – Brian