Kolekcje w Praktyce: Memoizacja.
Zadanie 5.4: Praktyczne Zastosowanie Map (Cache)
Wstęp
Wyobraź sobie funkcję, która wykonuje bardzo skomplikowane obliczenia (np. liczy silnię z ogromnej liczby albo pobiera dane z wolnego serwera). Jeśli wywołamy ją 10 razy z tym samym argumentem, marnujemy zasoby procesora na liczenie tego samego. Rozwiązaniem jest Memoizacja (Caching) – zapamiętanie wyniku dla danego argumentu, aby następnym razem zwrócić go natychmiast z pamięci.
Cel zadania
Stworzenie funkcji wyższego rzędu (dekoratora), która dodaje mechanizm cache do dowolnej innej funkcji, wykorzystując Map.
Wymagania techniczne
- Stworzenie "wolnej" funkcji do testów.
- Zaimplementowanie funkcji
memoize(fn). - Użycie
Mapdo przechowywania pary Argument -> Wynik.
Kroki do wykonania
1. Problem: Wolna funkcja
Stwórz plik cache.js i symuluj wolną funkcję.
function slowDouble(num) {
console.log(`Liczenie dłuuugo dla ${num}...`);
// Symulacja obciążenia (pętla)
for(let i=0; i<1e8; i++) {}
return num * 2;
}
console.log(slowDouble(5));
console.log(slowDouble(5)); // Znowu liczy długo...
2. Rozwiązanie: Funkcja memoize
Tworzymy funkcję, która przyjmuje funkcję fn i zwraca nową, "ulepszoną" funkcję.
function memoize(fn) {
const cache = new Map(); // Tutaj trzymamy wyniki
return function(arg) {
// 1. Sprawdź czy wynik już jest w cache
if (cache.has(arg)) {
console.log("Pobieranie z cache dla:", arg);
return cache.get(arg);
}
// 2. Jeśli nie ma, wykonaj funkcję
const result = fn(arg);
// 3. Zapisz wynik w cache na przyszłość
cache.set(arg, result);
return result;
};
}
3. Testowanie
Teraz "opakujmy" naszą wolną funkcję.
const fastDouble = memoize(slowDouble);
console.log("Pierwsze wywołanie:");
console.log(fastDouble(5)); // Liczy długo...
console.log("Drugie wywołanie:");
console.log(fastDouble(5)); // Błyskawicznie z cache!
console.log("Inny argument:");
console.log(fastDouble(10)); // Liczy długo...
console.log(fastDouble(10)); // Błyskawicznie z cache!
4. Zadanie praktyczne
Stwórz kalkulator silni (factorial), który używa rekurencji.
- Napisz zwykłą funkcję
factorial(n). - Użyj funkcji
memoize, aby stworzyćcachedFactorial. - Oblicz
cachedFactorial(5), a potemcachedFactorial(6).- Zauważ, że
cachedFactorial(6)to6 * cachedFactorial(5). Czy Twój cache potrafi to wykorzystać? (W podstawowej wersjimemoizepowyżej - nie, ponieważ cache jest zewnętrzny. Aby to działało idealnie rekurencyjnie, funkcja musiałaby wywoływać samą siebie już w wersji zcache'owanej, ale to zadanie na poziom expert!).
- Zauważ, że
- Dla celów tego zadania wystarczy, że po obliczeniu
cachedFactorial(5)ponowne wywołaniecachedFactorial(5)będzie szybkie.
[!TIP]
Mapjest idealna do cache'owania, bo klucze są unikalne i dostęp do nich jest bardzo szybki (złożoność O(1)).
[!IMPORTANT] Commit: Zadanie 5.4 - Cache System.