Friday, May 10, 2024

📑 Memorization Decorators

📑 Task

1) What is the decorator used for in the second function definition?
2) Can you write your own decorator for the same goal?
import functools, time
def fibonacci1(n):
    if n < 2: return n
    return fibonacci1(n - 1) + fibonacci1(n - 2)
# a least-recently-used (LRU) cache eviction policy
@functools.lru_cache(maxsize=None)
def fibonacci2(n):
    if n < 2: return n
    return fibonacci2(n - 1) + fibonacci2(n - 2)
code_str = lambda f, n: f"""
start = time.time(); {f(n)}; stop = time.time() 
print(f'{f}: {{stop - start: e}}')"""
for f in [fibonacci1, fibonacci2]:
    exec(code_str(f, 30))

📑 Answer

1) The memoization provided by `functools.lru_cache` prevents redundant calculations
by storing previously computed Fibonacci numbers and
retrieving them from the cache when needed
2) The decorator with custom cache eviction policies and
different data structures for the cache
def cache(func):
    @functools.wraps(func)
    def wrapper_cache(*args, **kwargs):
        cache_key = args + tuple(kwargs.items())
        if cache_key not in wrapper_cache.cache:
            wrapper_cache.cache[cache_key] = func(*args, **kwargs)
        return wrapper_cache.cache[cache_key]
    wrapper_cache.cache = dict()
    return wrapper_cache
@cache
def fibonacci3(n):
    if n < 2: return n
    return fibonacci3(n - 1) + fibonacci3(n - 2)
for f in [fibonacci1, fibonacci2, fibonacci3]:
    exec(code_str(f, 30))

No comments:

Post a Comment