Memoization
What is Memoization
Memoization is an optimization technique.
It used to store the output of a function so that it can be directly returned from the cache when the function is called again with the same input parameters, without having to re-run the function.
Implementation I
Implement a memoization mechanism that fulfills the following code.
let callCount = 0;
const memoizedFn = memoize(function (a, b) {
callCount += 1;
return a + b;
});
memoizedFn(2, 3); // 5
memoizedFn(2, 3); // 5
console.log(callCount); // 1
Approach
- Initialize a cache object to store calculated value.
- Convert the passed arguments into a string as key of cache object.
- Check if the key already exists in the cache. If so, return the associated value immediately.
- Otherwise, call the provided function, store the output in the cache, and return the output.
function memoize(cb) {
// Initialize a cache object to store calculated value
const cache = {};
// Create a closure that allows all subsequently called functions to access the same cache object
return function (...args) {
// Convert the passed arguments into a string as key of cache object
const key = JSON.stringify(args);
// Check if the key already exists in the cache
if (key in cache) {
// If so, return the associated value immediately
return cache[key];
}
// Otherwise, call the provided function, store the output in the cache, and return the output
const result = cb.apply(this, args);
cache[key] = result;
return result;
};
}
Implementation II
實作一個緩存機制,引數可以接受任何型別(Primitive + Reference)。
思路
- 建立一個
TrieNode
節點,每個節點包含一個子節點children
,以及用來儲存計算過的結果value
。 - 由於
Map
的 key 可以儲存字串以外的型別,因此如果傳入的引數為物件或陣列,可以用來判斷是否為同一個物件或陣列。 - 每次函式被呼叫時,遍歷引數,並檢查
children
內是否有與引數相同的 key,如果沒有則建立新的節點,並更新目前的節點。 - 檢查
value
是否有值,如有值,直接返回計算過的值,如果沒值,則透過該引數計算出結果並儲存在value
,然後回傳結果。
class TrieNode {
constructor() {
this.children = new Map();
this.value = null;
}
}
function memoize(cb) {
// 建立 TrieNode 用來儲存之前計算過的結果
const cache = new TrieNode();
// 建立閉包,讓接下來呼叫的函式都能取得相同 cache 物件
return function (...args) {
// 目前的節點
let current = cache;
// 遍歷引數,檢查 children 內是否有與引數相同的 key
for (const arg of args) {
// 如果沒有則建立新的節點
if (!current.children.has(arg)) {
current.children.set(arg, new TrieNode());
}
// 更新目前的節點
current = current.children.get(arg);
}
// 檢查 value 是否有值
// 如果沒值,則透過該引數計算出結果並儲存在 value
if (current.value == null) {
current.value = cb(...args);
}
// 回傳結果
return current.value;
};
}