web-dev-qa-db-ja.com

関数の機能はどのように機能しますか?

私の知る限り、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を提供します。なぜ7206も階乗を取るようです。しかし、その理由を理解する必要があります。

誰かがこれが起こる理由と回避策を説明できますか?

基本的に、リスト内のすべてのエントリの階乗の積を計算します。バックアップ計画は、ループを実行して計算することです。ただし、reduceを使用することをお勧めします。

40
Divya

はい、わかった:

最初に数値を階乗にマッピングし、次に乗算演算子でreduceを呼び出す必要があります。

だから、これはうまくいく:

lst_fact = map(fact, lst)
reduce(operator.mul, lst_fact)
2
Divya

他の答えは素晴らしいです。単にreduce()を理解するのに非常に良いと思われる図解された例を追加します。

>>> reduce(lambda x,y: x+y, [47,11,42,13])
113

次のように計算されます。

enter image description here

ソース )( ミラー

68

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を生成します:-)

30

関数は、両方の引数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番目の呼び出しに渡されると、乗算の前に階乗化されることを見逃しました。

9
Brooks Moses

まず、最初に_reduce_func_にはフォールドの構造がありません。折り目の説明と一致しません(正しい)。

フォールドの構造は次のとおりです。def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)

これで、fact関数は2つの要素を操作しません-階乗を計算するだけです。

つまり、要するに、フォールドを使用しておらず、その階乗の定義を使用する必要はありません。

階乗を試してみたい場合は、yコンビネーターをチェックしてください: http://mvanier.livejournal.com/2897.html

折り畳みについて知りたい場合は、この質問への私の答えを見てください。累積分数を計算するための使用を示しています: データの辞書から累積パーセンテージを作成する

0
Marcin

些細な例に加えて、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)]
0
Pierre D

Reduceを使用して階乗を実装することもできます。

def factorial(n):
  return(reduce(lambda x,y:x*y,range(n+1)[1:]))
0
passmaster10

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)
0