コアポイント
プログラムは、同じ結果を繰り返し再計算する関数を呼び出す時間を無駄にします。これは、再帰的および数学的な機能に特に当てはまります。フィボナッチ数ジェネレーターは完璧な例です。フィボナッチシーケンスは、ゼロサムから始まる一連の整数であり、各値はシーケンス内の最初の2つの数値の合計です。この定義によると、最初の10個のフィボナッチ数は次のとおりです。0、1、1、2、3、5、8、13、21、34。プログラミングの観点から、フィボナッチ数は通常、次の関数を使用して再帰的に計算されます。 この関数は、より小さな「n」値に対してうまく機能します。ただし、「N」が増加すると、パフォーマンスは急速に低下します。これは、2つの再帰的な呼び出しが同じ作業を繰り返すためです。たとえば、50番目のフィボナッチ数を計算するには、再帰関数を40億回以上呼び出す必要があります(正確には40,730,022,147)!さらに悪いことに、51番目の数値を計算するには、作業をほぼ2回繰り返す必要があります。関数が以前に計算したことを覚えている場合、繰り返し作業の問題を軽減できます。
function fibonacci(n) { if (n === 0 || n === 1) return n; else return fibonacci(n - 1) + fibonacci(n - 2); }
暗記は、関数の以前の計算の結果をキャッシュすることにより、関数のパフォーマンスを改善しようとするプログラミング手法です。 JavaScriptオブジェクトは連想配列のように振る舞うため、キャッシュとして機能するのに理想的です。メモリ関数が呼び出されるたびに、そのパラメーターはインデックスキャッシュに使用されます。データが存在する場合、関数全体を実行せずに返すことができます。ただし、データがキャッシュされていない場合、関数が実行され、結果がキャッシュに追加されます。 次の例では、元のフィボナッチ関数を書き直してメモリを含めます。この例では、自己執行匿名関数は、フィボナッチ関数として使用される内部関数f()を返します。 f()が返されると、その閉鎖により、以前のすべての結果を保存する「メモ」オブジェクトへのアクセスを継続できます。 f()が実行されるたびに、最初に現在の「n」値の結果が存在するかどうかを確認します。存在する場合、キャッシュ値が返されます。それ以外の場合は、元のFibonacciコードを実行します。 「メモ」はf()の外側で定義されているため、複数の関数呼び出しで値を保持できることに注意してください。元の再帰関数は、50番目のフィボナッチ数を計算する前に400億回以上呼び出されたことを思い出してください。メモリを実装することにより、この数値は99に低下します。 複数のパラメーターの処理 前の例では、関数は単一のパラメーターを受け入れます。これにより、キャッシュの実装が非常に簡単になります。残念ながら、ほとんどの関数には複数のパラメーターが必要であり、キャッシュされたインデックスを複雑にします。複数のパラメーターを使用して関数を記憶するには、キャッシュを多次元にする必要があります。または、すべてのパラメーターを組み合わせて単一のインデックスを形成する必要があります。 多次元法では、キャッシュは単一のオブジェクトではなく、オブジェクトの階層になります。次に、各ディメンションは単一のパラメーターによってインデックス付けされます。次の例では、フィボナッチ関数の多次元キャッシュを実装しています。この例では、関数は追加のパラメーター「x」を受け入れ、何もしません。関数が呼び出されるたびに、コードは「x」ディメンションが存在するかどうかをチェックし、存在しない場合に初期化します。それ以来、「x」ディメンションを使用して「n」値をキャッシュします。その結果、関数はFibonacci( "foo"、3)とFibonacci( "bar"、3)を呼び出しても同じ結果とは見なされません。 多次元キャッシュに代わるものは単一のキャッシュオブジェクトで、関数のすべてのパラメーターの組み合わせによってインデックス付けされます。この方法では、パラメーターは配列に変換され、インデックスキャッシュに使用されます。各関数には、パラメーターに合格した「引数」という名前の組み込みオブジェクトがあります。 「引数」は、クラスの配列オブジェクトと呼ばれるタイプのオブジェクトです。アレイに似ていますが、インデックスキャッシュには使用できません。したがって、最初に実際の配列に変換する必要があります。これは、配列スライス()メソッドを使用して実行できます。以前に示すように、キャッシュを配列表現を使用してインデックス化できます。次の例は、これを達成する方法を示しています。追加の変数「スライス」は、配列スライス()メソッドへの参照として定義されていることに注意してください。このリファレンスを保存することにより、array.prototype.slice()の繰り返し計算を避けることができます。次に、call()メソッドを使用して、slice()を「引数」に適用します。 オブジェクトパラメーターをキャッシュ ここに導入されたメモリスキームは、オブジェクトパラメーターをうまく処理しません。オブジェクトがインデックスとして使用される場合、最初に「[オブジェクトオブジェクト]」などの文字列表現に変換されます。これにより、複数のオブジェクトが同じキャッシュの場所に誤ってマッピングされます。この動作は、インデックス作成の前にオブジェクトパラメーターを描画することで修正できます。残念ながら、これはメモリプロセスも遅くなります。次の例では、オブジェクトを引数として取る一般的なメモリ関数を作成します。オブジェクトパラメーターは、json.stringify()を使用してstringifiedを使用してキャッシュされたインデックスを作成することに注意してください。 自動メモリ 以前のすべての例では、メモリを追加するように関数が明示的に変更されます。暗記されたインフラストラクチャも、機能を変更せずに実装できます。これは、関数ロジックを記憶されたロジックとは別に実装できるようにするため有用です。これは、関数を入力として使用し、それを適用して記憶するために適用するユーティリティ関数を作成することによって行われます。次のMemoize()関数は、関数「FUNC」を入力として使用します。 Memoize()は、「FUNC」にキャッシュメカニズムをラップする新しい関数を返します。この関数はオブジェクトパラメーターを処理しないことに注意してください。オブジェクトを処理するには、各パラメーターを個別にチェックし、必要に応じてStringifyを確認するループが必要です。 制限 メモリを実現する場合、次のポイントを記憶する必要があります。まず、古い結果を保存することにより、記憶関数は余分なメモリを消費します。フィボナッチの例では、余分なメモリ消費量は無制限です。メモリの使用が問題の場合、固定サイズのキャッシュを使用する必要があります。メモリに関連付けられているオーバーヘッドは、非常に迅速に実行したり、頻度が低い関数に適していない場合もあります。 function fibonacci(n) {
if (n === 0 || n === 1)
return n;
else
return fibonacci(n - 1) + fibonacci(n - 2);
}
var fibonacci = (function() {
var memo = {};
function f(n) {
var value;
if (n in memo) {
value = memo[n];
} else {
if (n === 0 || n === 1)
value = n;
else
value = f(n - 1) + f(n - 2);
memo[n] = value;
}
return value;
}
return f;
})();
var fibonacci = (function() {
var memo = {};
function f(x, n) {
var value;
memo[x] = memo[x] || {};
if (x in memo && n in memo[x]) {
value = memo[x][n];
} else {
if (n === 0 || n === 1)
value = n;
else
value = f(x, n - 1) + f(x, n - 2);
memo[x][n] = value;
}
return value;
}
return f;
})();
function fibonacci(n) {
if (n === 0 || n === 1)
return n;
else
return fibonacci(n - 1) + fibonacci(n - 2);
}
var fibonacci = (function() {
var memo = {};
function f(n) {
var value;
if (n in memo) {
value = memo[n];
} else {
if (n === 0 || n === 1)
value = n;
else
value = f(n - 1) + f(n - 2);
memo[n] = value;
}
return value;
}
return f;
})();
var fibonacci = (function() { var memo = {}; function f(x, n) { var value; memo[x] = memo[x] || {}; if (x in memo && n in memo[x]) { value = memo[x][n]; } else { if (n === 0 || n === 1) value = n; else value = f(x, n - 1) + f(x, n - 2); memo[x][n] = value; } return value; } return f; })();
に記憶する
FAQ暗記は、JavaScriptやその他の言語のコンピュータープログラムを最適化するために使用されるプログラミング手法です。この手法では、同じ入力が再び表示されたときに、高価な関数呼び出しの結果を保存し、それらを再利用することが含まれます。これにより、不必要な計算を回避することにより、プログラムのパフォーマンスが大幅に向上する可能性があります。同じパラメーターで繰り返し呼び出される再帰関数または関数がある状況で特に役立ちます。
JavaScriptで記憶された機能の例を提供できますか?
function fibonacci(n) { if (n === 0 || n === 1) return n; else return fibonacci(n - 1) + fibonacci(n - 2); }
JavaScriptでメモリを使用することに制限や短所はありますか?
var fibonacci = (function() { var memo = {}; function f(n) { var value; if (n in memo) { value = memo[n]; } else { if (n === 0 || n === 1) value = n; else value = f(n - 1) + f(n - 2); memo[n] = value; } return value; } return f; })();
JavaScriptのあらゆる種類の機能に暗記を使用できますか?
複数のパラメーターを使用した関数のメモリの実装は少し複雑になる可能性がありますが、これは間違いなく可能です。 1つの方法は、引数をキャッシュのキーとして使用できる文字列に変換することです。例は次のとおりです。
function fibonacci(n) { if (n === 0 || n === 1) return n; else return fibonacci(n - 1) + fibonacci(n - 2); }
はい、JavaScriptで記憶するのに役立つライブラリとツールがあります。たとえば、人気のあるJavaScriptユーティリティライブラリLodashは、機能を簡単に記憶できる_.memoize
関数を提供します。同様に、RAMDAライブラリは、カスタムキャッシュキー関数を指定できるR.memoizeWith
関数を提供します。
メモリ関数のキャッシュは、キャッシュオブジェクトをリセットするだけでクリアできます。例は次のとおりです。
var fibonacci = (function() { var memo = {}; function f(n) { var value; if (n in memo) { value = memo[n]; } else { if (n === 0 || n === 1) value = n; else value = f(n - 1) + f(n - 2); memo[n] = value; } return value; } return f; })();
はい、メモリはReactなどのJavaScriptフレームワークで非常に役立ちます。たとえば、Reactはコンポーネントを記憶するために使用できるReact.memo
関数を提供します。これは、コンポーネントの不必要に再レンダリングを防ぐことにより、パフォーマンスを改善するのに役立ちます。
記憶は強力な最適化手法ですが、必ずしも最良の解決策ではありません。場合によっては、dejitterやスロットリングなどの他の最適化技術がより適切になる場合があります。重要なのは、プログラムの特定のニーズと制約を理解し、適切な最適化テクノロジーを選択することです。
以上がJavaScriptにメモを実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。