質問: 関数を構成する2つの方法、効率の違いは? fがある値を別の値に変換するようにしてから、変換をn回繰り返す関数を書いています。 私は二つの異なる方法を考え出しました: 1つは文字通り関数をn回適用する明白な方法です、従って* repeat(f、4)は x→f(f(f(f(f))))*を意味します もう1つの方法は、力を与えるための高速な方法からヒントを得たものです。これは、nが偶数である場合は常に、問題を半分の大きさの2つの問題に分割することを意味します。 だから* repeat(f、4)は x→g(g(x))を意味します*ここで* g(x)= f(f(x))* 最初は、2番目の方法では効率はそれほど向上しないと思いました。 一日の終わりには、まだf n回適用する必要がありますよね。 上記の例では、* g はそれ以上の単純化なしに f o f *に変換されます。 しかし、私がメソッドを試したとき、後者のメソッドは早く目立ちました。 ;; 2つの関数の合成を計算します(定義(f g)(lambda(x)(f(g x))))。 ;;識別関数(define(id x)x) ;;素朴な方法で関数の適用を繰り返す) ;;関数の適用を繰り返し、nを征服するように分割する(define(repeat2 f n)(define iter f k acc)(cond((= k 0)acc)) k)(iter(f fを構成)(/ k 2)acc))(else(it f( - k 1)(f accを構成)))))(it f n id)) ;;テストに使用される増分関数(define(inc x)(x 1)) 実際、((repeat2 inc 1000000)0)*は((repeat1 inc 1000000)0)*よりはるかに高速でした。 私の質問は、2番目の方法が1番目の方法よりも効率的だったという点です。 同じ関数オブジェクトを再利用すると、記憶域が節約され、新しいオブジェクトの作成にかかる時間が短縮されますか 結局のところ、アプリケーションをn回繰り返す必要があります。つまり、* x→((x 1)1)を自動的に x→(x 2)*に減らすことはできません。 私はdrscheme 4.2.1を実行しています。 どうもありがとうございました。
回答: どちらのバージョンでも同じ回数の `inc`を呼び出すのは正しいことですが、コードにはそれよりも多くのオーバーヘッドがあります。 具体的には、最初のバージョンではn個のクロージャが作成されますが、2番目のバージョンではlog(n)個のクロージャのみが作成されます。 これを詳細に見るために使用できるものが3つあります。 速度を測定するには、drschemeの `time`特殊形式を使用してください。 計算にかかる時間に加えて、gcにどのくらいの時間がかかったかもわかります。 あなたは、最初のバージョンがgc作業をしているのに対し、2番目のバージョンはしていないことがわかります。 (まあ、それはしますが、それは非常に小さいので、おそらく表示されないでしょう。) あなたの `inc`関数はほんの少ししかしていないので、あなたはループのオーバーヘッドだけを測定しています。 たとえば、私がこの悪いバージョンを使うとき: (define(slow-inc x)(define(plus1 x)(/(if(<(random 10)5)(*(x 1)2)(2)(* x 2)2))2))2))) - - (plus1 (プラス1(プラス1×))2)) この2つの用途の違いは、約11から1.6に低下します。 . 最後に、このバージョンを試してみてください。 (define(repeat 3 f n)(λ(x)(define iter n x)(if(zero? n)x(iter(sub 1 n)(f x))))(iter n x))) それはどんな構成もしません、そしてそれはあなたの2番目のバージョンとほぼ同じ速度で動作します。