こんにちは、Habrahabrの親愛なる読者。 この記事では、メモ化とカリー化とは何か、これらのメソッドがPython標準ライブラリでどのように実装されるかを理解しようとします。
メモ化
これは、関数実行の結果が保存され、この結果が次の呼び出しで使用される最適化方法です。
フィボナッチ数を見つける再帰的な実装を使用して、実行時間を調べます。
@clock def fib(n): if n < 2: return n return fib(n-2) + fib(n-1) print('fib(20) =', fib(20))
[0.35938287s] fib(20) -> 6765
@clock def clock(func): def clocked(*args, **kwargs): t0 = time.time() result = func(*args, **kwargs)

検出する必要がある数が増えると、ランタイムは非常に急速に成長し、RecursionErrorエラーが発生する可能性があります。
このようなアルゴリズムを最適化するには、メモ化メソッドが適しています。つまり、初期の計算値を保存して再利用します(ただし、もちろん、最初から再帰アルゴリズムを完全に放棄する必要があります)。
_fib_cache = {1: 1, 2: 1}
[0.03125453s] mem_fib(200) -> 280571172992510140037611932413038677189525
またはデコレーターとして:
def memoize(f): cache = {} def decorate(*args): if args in cache: return cache[args] else: cache[args] = f(*args) return cache[args] return decorate
良いニュースは、lru_cacheデコレータが標準のfunctoolsライブラリに既に十分に実装されていることです。
LRUは、Least Recent Usedの略です。
from functools import lru_cache @clock @lru_cache() def fib(n): if n < 2: return n return fib(n-2) + fib(n-1) print('fib(20) =', fib(20))
lru_cacheには2つのオプションの引数があります。
maxsizeは、保存された結果の数です。
typed-trueの場合、たとえば、値1と1.0は異なると見なされます。
メモ化は非常にシンプルで効果的な方法です。 functools.lru_cacheのおかげで、Pythonで使用すると便利です。 内部では、ハッシュテーブル形式の辞書がそれぞれあり、キーはハッシュを実装する必要があります。
カレーまたはカレーについて
カリー化とは、多くの引数から関数のセットへの関数の変換であり、各関数は1つの引数の関数です。 引数の一部を関数に渡し、残りの引数を期待する関数を取得できます。
挨拶と名前を引数として取る簡単な挨拶関数を作成しましょう。
def greet(greeting, name): print(greeting + ', ' + name) greet('Hello', 'German')
少し改善することで、あらゆる種類のグリーティング用の新しい関数を作成し、この新しい関数に名前を渡すことができます。
def greet_curried(greeting): def greet(name): print(greeting + ', ' + name) return greet greet_hello = greet_curried('Hello') greet_hello('German') greet_hello('Ivan')
そして、任意の数の引数でこれを行うことができます:
def greet_deeply_curried(greeting): def w_separator(separator): def w_emphasis(emphasis): def w_name(name): print(greeting + separator + name + emphasis) return w_name return w_emphasis return w_separator greet = greet_deeply_curried("Hello")("...")(".") greet('German') greet('Ivan')
または、ラムダを持つバリアント:
greet_deeply_curried = lambda greeting: lambda separator: lambda emphasis: lambda name: \ print(greeting + separator + name + emphasis)
部分適用
これは、引数の一部に関数を適用するプロセスです。
言い換えると、いくつかのパラメーターを持つ関数を取り、より少ないパラメーターを持つ関数を返す関数です。
部分的なアプリケーションは、関数をn個の引数から(xn)に変換し、カリー化は1個の引数を持つn個の関数を作成します。
Pythonは標準のfunctoolsライブラリにこのような機会があります。これは関数です
部分的。
from functools import partial def greet(greeting, separator, emphasis, name): print(greeting + separator + name + emphasis) newfunc = partial(greet, greeting='Hello', separator=',', emphasis='.') newfunc(name='German') newfunc(name='Ivan') newfunc2 = partial(greet, greeting='Hello', emphasis='.') newfunc2(name='German', separator='...') newfunc2(name='Ivan', separator='..')
別の部分的な例では、ループクロージャの問題を解決します。
from functools import partial def makeActions(): acts = [] for i in range(5): def func(x, y): return x * y acts.append(partial(func, y=i))
今日は以上です。 よろしくお願いします!