2011-08-17 11 views
18

2回目の呼び出しが最大10倍遅くなる可能性があるため、JavaScriptで関数を再帰的に呼び出すときに軽く踏んでください。JavaScriptでの再帰関数の呼び出し

Eloquent JavaScript状態:

一つの重要な問題があります:ほとんどのJavaScriptの実装では、この第二のバージョンは、最初のものよりも約10倍遅いです。 JavaScriptでは、単純なループを実行することは、関数を複数回呼び出すよりもずっと安価です。

John Resigもこれがthis投稿の問題です。

私の質問です:再帰を使うのはなぜそれほど効率が悪いのですか?特定のエンジンが構築された方法ですか? JavaScriptでこれが当てはまらない時がありますか?

+1

私はそれがスコープのものだと推測しています:P大きな質問、私は彼らが何を答えているのが不思議です! – Pelshoff

+4

関数呼び出しのオーバーヘッドはループ制御のオーバーヘッドよりも単純です。これはまさにあらゆるプログラミング言語に当てはまります。新しいスコープの割り当てと初期化、戻りアドレスの保存、パラメータの整列化など、関数を呼び出すときに行うべきより多くの簿記があります.JavaScriptインタープリタは非常に速いペースで高速化しています。 (または本)に質問する必要があります。 – Pointy

+1

"2回目の呼び出しが最大10倍遅くなる可能性があるため"これはテキストの内容とは異なります。コードの2番目のバージョンは10倍遅いと言われています。 –

答えて

11

関数呼び出しは、スタックを変更して新しいコンテキストを設定するなどのオーバーヘッドがあるため、単純なループよりもコストがかかります。再帰が非常に効率的になるためには、ある種の再帰関数をループに変換することを基本とするテールコール除去の何らかの形をサポートしなければなりません。 OCaml、Haskell、Schemeのような関数型言語はこれを行いますが、私が知っているJavaScriptの実装はありません。(そうしなければ、ほんのわずかしか役に立たないので、食事の哲学者の問題があるかもしれません。

+5

ES6にテールコールの最適化が必要です。 [適切なテールコール](http://wiki.ecmascript.org/doku.php?id=harmony:proper_tail_calls) – Raynos

3

これは、ブラウザが使用する特定のJSエンジンが構築されている方法です。テールコールの消去を行わないと、ループを繰り返すたびに新しいスタックフレームを作成する必要がありますが、ループを使用すると、プログラムカウンタを開始位置に戻すだけです。例えば、Schemeは言語仕様の一部としてこれを持っているので、パフォーマンスを心配することなく再帰をこの方法で使うことができます。

https://bugzilla.mozilla.org/show_bug.cgi?id=445363は、Firefoxで進歩していることを示しています(Brendan Eich氏はECMAScript仕様の一部となっている可能性があると話しています)。しかし、現在のブラウザではこれがまだ実現していません。

関連する問題