私の知る限り、reduce関数はリストl
と関数f
を取ります。次に、リストの最初の2つの要素で関数f
を呼び出し、次のリスト要素と前の結果で関数f
を繰り返し呼び出します。
そこで、次の関数を定義します。
次の関数は階乗を計算します。
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return fact(x) * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
さて、これは私に((1! * 3!) * 1!) = 6
を与えるべきではありませんか?しかし、代わりに720
を提供します。なぜ720
? 6
も階乗を取るようです。しかし、その理由を理解する必要があります。
誰かがこれが起こる理由と回避策を説明できますか?
基本的に、リスト内のすべてのエントリの階乗の積を計算します。バックアップ計画は、ループを実行して計算することです。ただし、reduceを使用することをお勧めします。
はい、わかった:
最初に数値を階乗にマッピングし、次に乗算演算子でreduceを呼び出す必要があります。
だから、これはうまくいく:
lst_fact = map(fact, lst)
reduce(operator.mul, lst_fact)
reduce()を理解する最も簡単な方法は、その純粋なPython同等のコードを見ることです:
def myreduce(func, iterable, start=None):
it = iter(iterable)
if start is None:
try:
start = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = start
for x in iterable:
accum_value = func(accum_value, x)
return accum_value
Reduce_func()が右端の引数に階乗を適用することだけが理にかなっていることがわかります:
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return x * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
その小さなリビジョンでは、コードは予想どおり6を生成します:-)
関数は、両方の引数でfact()
を呼び出します。 _((1! * 3!)! * 1!)
_を計算しています。回避策は、2番目の引数でのみ呼び出し、reduce()
に1の初期値を渡すことです。
Python reduce
documentation から、
reduce(function、sequence)は、シーケンスの最初の2つの項目、次に結果と次の項目などで(バイナリ)関数を呼び出すことによって構築された単一の値を返します。
だから、ステップスルー。最初の2つの要素reduce_func(1, 3) = 1! * 3! = 6
の_reduce_func
_を計算します。次に、結果の_reduce_func
_と次の項目reduce_func(6, 1) = 6! * 1! = 720
を計算します。
最初の_reduce_func
_呼び出しの結果が入力として2番目の呼び出しに渡されると、乗算の前に階乗化されることを見逃しました。
まず、最初に_reduce_func
_にはフォールドの構造がありません。折り目の説明と一致しません(正しい)。
フォールドの構造は次のとおりです。def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)
これで、fact
関数は2つの要素を操作しません-階乗を計算するだけです。
つまり、要するに、フォールドを使用しておらず、その階乗の定義を使用する必要はありません。
階乗を試してみたい場合は、yコンビネーターをチェックしてください: http://mvanier.livejournal.com/2897.html
折り畳みについて知りたい場合は、この質問への私の答えを見てください。累積分数を計算するための使用を示しています: データの辞書から累積パーセンテージを作成する
些細な例に加えて、reduce
が実際に非常に有用であることがわかった例を次に示します。
順序付けられたint
値の反復可能なものを想像してください。多くの場合、連続する値の実行があり、範囲を表すタプルのリストとして「要約」したいと思います。 (この反復可能オブジェクトは、非常に長いシーケンスのジェネレーターになる可能性があることに注意してください。これは、メモリ内コレクションに対する何らかの操作ではなく、reduce
を使用する別の理由です)。
from functools import reduce
def rle(a, b):
if a and a[-1][1] == b:
return a[:-1] + [(a[-1][0], b + 1)]
return a + [(b, b + 1)]
reduce(rle, [0, 1, 2, 5, 8, 9], [])
# [(0, 3), (5, 6), (8, 10)]
適切なinitial
値([]
ここ)reduce
の場合。
同様に処理されるコーナーケース:
reduce(rle, [], [])
# []
reduce(rle, [0], [])
# [(0, 1)]
Reduceを使用して階乗を実装することもできます。
def factorial(n):
return(reduce(lambda x,y:x*y,range(n+1)[1:]))
Reduceは、パラメーター#2の反復子によって提供された値を使用して、パラメーター#1の関数を連続して実行します。
print '-------------- Example: Reduce(x + y) --------------'
def add(x,y): return x+y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 0
for i in range(a,b):
tot = tot+i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)
print '-------------- Example: Reduce(x * y) --------------'
def add(x,y): return x*y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 1
for i in range(a,b):
tot = tot * i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)