回答:
reduce
そしてapply
もちろんのみ同等である可変アリティの場合、すべての引数を参照する必要が連想機能のための(最終的な結果の観点では、返されました)。それらが結果的に同等である場合、私はそれapply
が常に完全に慣用的である一方reduce
で、同等であり、多くの一般的なケースで瞬きのほんの一部を削り取る可能性があると言います。以下はこれを信じる私の理論的根拠です。
+
reduce
可変アリティの場合(2つ以上の引数)に関してそれ自体が実装されています。確かに、これは可変アリアの連想関数reduce
を実行するための非常に賢明な「デフォルト」の方法のように見えます。物事をスピードアップするためにいくつかの最適化を実行する可能性internal-reduce
があります。うまくいけば、将来的に再導入される予定です-可変引数の場合にそれらから利益を得る可能性のあるすべての関数で複製することは愚かなことです。このような一般的なケースでapply
は、わずかなオーバーヘッドが追加されます。(実際に心配する必要はありません。)
一方、複雑な関数は、に組み込むには一般的ではないいくつかの最適化の機会を利用する場合がありreduce
ます。その後、実際にあなたを遅くするかもしれないapply
間、あなたはそれらを利用することができますreduce
。後者のシナリオが実際に発生する良い例を以下に示しますstr
。これはStringBuilder
内部で使用するため、apply
ではなくを使用することで大きなメリットを得られますreduce
。
だから、私apply
は疑わしいときに使用すると言います。そして、それがあなたに何も買わないことを知っている場合reduce
(そして、これがすぐに変わる可能性は低いことを知っている場合)、reduce
必要に応じて、その小さな不要なオーバーヘッドを取り除いてください。
sum
:、私はClojureにこの関数がある+
と思いますapply
。それは呼び出され、で使用できます。:-)真剣に言えば、Lispでは、一般に、可変個の関数が提供されている場合、通常、コレクションを操作するラッパーを伴わないと思います-これは、使用するものですapply
(またはreduce
、それがより理にかなっている場合)。
reduce
疑わしいときはapply
、最適化があると確信しているとき。reduce
の契約はより正確であり、一般的な最適化が行われやすくなっています。apply
よりあいまいなので、ケースバイケースでのみ最適化できます。str
とconcat
2つの一般的な例外です。
reduce
し、apply
結果の面で同等であり、私は彼らの可変長の過負荷を最適化する最善の方法を知っているだけの用語の中でそれを実装するために、当該機能の作者を期待するreduce
場合それが実際に最も意味のあることです(そうするためのオプションは確かに常に利用可能であり、非常に実用的なデフォルトになります)。ただし、あなたがどこから来たのかreduce
は確かにわかりますが、Clojureのパフォーマンスストーリーの中心であり(ますますそうなっています)、非常に高度に最適化され、非常に明確に指定されています。
+のような単純な関数を使用する場合、どの関数を使用してもかまいません。
一般的には、これreduce
は累積演算です。現在の累積値と1つの新しい値を累積関数に提示します。関数の結果は、次の反復の累積値です。したがって、反復は次のようになります。
cum-val[i+1] = F( cum-val[i], input-val[i] ) ; please forgive the java-like syntax!
適用するためのアイデアは、多数のスカラー引数を期待する関数を呼び出そうとしているが、それらは現在コレクション内にあり、引き出す必要があるということです。だから、言う代わりに:
vals = [ val1 val2 val3 ]
(some-fn (vals 0) (vals 1) (vals 2))
言うことが出来る:
(apply some-fn vals)
そしてそれは以下と等価になるように変換されます:
(some-fn val1 val2 val3)
したがって、「適用」の使用は、シーケンスの周りの「括弧の削除」のようなものです。
少し遅れましたが、この例を読んだ後、簡単な実験を行いました。これは私のreplの結果です。応答から何も推測できませんが、reduceとapplyの間にある種のキャッシングキックがあるようです。
user=> (time (reduce + (range 1e3)))
"Elapsed time: 5.543 msecs"
499500
user=> (time (apply + (range 1e3)))
"Elapsed time: 5.263 msecs"
499500
user=> (time (apply + (range 1e4)))
"Elapsed time: 19.721 msecs"
49995000
user=> (time (reduce + (range 1e4)))
"Elapsed time: 1.409 msecs"
49995000
user=> (time (reduce + (range 1e5)))
"Elapsed time: 17.524 msecs"
4999950000
user=> (time (apply + (range 1e5)))
"Elapsed time: 11.548 msecs"
4999950000
clojureのソースコードを見ると、internal-reduceでかなりクリーンな再帰が削減されますが、applyの実装には何も見つかりませんでした。適用のための+のClojure実装は、replによってキャッシュされるreduceを内部的に呼び出します。これは、4番目の呼び出しを説明しているようです。誰かが本当にここで何が起こっているのかを明確にできますか?
range
内に呼び出しを入れるべきではありませんtime
。シーケンス構築の干渉を取り除くためにそれを外側に置きます。私の場合、reduce
一貫してパフォーマンスが優れていapply
ます。
sum
余談ですが、haskellのような組み込み関数を含めてみませんか?かなり一般的な操作のようです。