2012-02-02 7 views
27

Per quanto ho capito, la funzione di riduzione prende una lista l e una funzione f. Quindi, chiama la funzione f sui primi due elementi dell'elenco e quindi chiama ripetutamente la funzione f con l'elemento dell'elenco successivo e il risultato precedente.Come funziona la funzione di riduzione?

Così, ho definire le seguenti funzioni:

La seguente funzione calcola il fattoriale.

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) 

Ora, questo non dovrebbe darmi ((1! * 3!) * 1!) = 6? Ma, invece, dà 720. Perché 720? Sembra prendere anche il fattoriale di 6. Ma, ho bisogno di capire perché.

Qualcuno può spiegare perché questo accada e una soluzione?

Principalmente voglio calcolare il prodotto di fattoriali di tutte le voci nell'elenco. Il piano di backup è di eseguire un ciclo e calcolarlo. Ma preferirei usare ridurre.

+0

Grazie a tutti. Ho capito la cosa sciocca che mi mancava. E ho pubblicato il modo corretto per farlo nelle risposte. – Divya

+0

Per una comprensione più approfondita di * reduce *, vedere il suo equivalente in python puro mostrato di seguito. –

risposta

0

Ok, ce l'ha:

ho bisogno di mappare i numeri per i loro fattoriali e poi chiamare a ridurre con operatore si moltiplicano.

Quindi, questo dovrebbe funzionare:

lst_fact = map(fact, lst) 
reduce(operator.mul, lst_fact) 
+0

Beh, sarebbe una specie di lavoro. La tua funzione fattoriale calcola ancora il fattoriale del suo input già, quindi la tua riduzione non è semplicemente farlo. – Marcin

+0

Sì, questo è un modo per farlo e probabilmente più "pulito" rispetto al calcolo fattoriale all'interno della funzione di riduzione, come suggerito da alcune delle altre risposte, ma entrambe faranno quello che vuoi. –

9

La funzione chiama fact() su entrambi gli argomenti. Stai calcolando ((1! * 3!)! * 1!). La soluzione è chiamare solo solo sul secondo argomento, e passare reduce() un valore iniziale di 1.

7

Dal Python reduce documentation,

ridurre (funzione, sequenza) restituisce un singolo valore ottenuto invocando la (binaria) sui primi due elementi della sequenza, quindi sul risultato e sull'elemento successivo, e così via.

Quindi, passando. Calcola reduce_func dei primi due elementi, reduce_func(1, 3) = 1! * 3! = 6. Quindi, calcola reduce_func del risultato e l'elemento successivo: reduce_func(6, 1) = 6! * 1! = 720.

Si è perso che, quando il risultato della prima chiamata reduce_func viene passato come input al secondo, viene fattorizzato prima della moltiplicazione.

0

Beh, prima di tutto, il tuo reduce_func non ha la struttura di una piega; non corrisponde alla descrizione di una piega (che è corretta).

La struttura di una piega è: def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)

Ora, la funzione fact non opera su due elementi - calcola solo fattoriale.

Quindi, in sintesi, non stai usando una piega, e con quella definizione di fattoriale, non è necessario.

Se si vuole giocare con fattoriale, controlla la y-Combinator: http://mvanier.livejournal.com/2897.html

Se volete conoscere le pieghe, guarda la mia risposta a questa domanda, che dimostra il suo utilizzo per calcolare le frazioni cumulativi : creating cumulative percentage from a dictionary of data

25

Il modo più semplice per capire ridurre() è quello di guardare al suo puro Python codice equivalente:

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 

Si può vedere che ha senso solo per il vostro reduce_func() per applicare il fattoriale per l'argomento più a destra:

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) 

Con quella piccola revisione, il codice produce come vi aspettavate :-)

+0

Hai appena fatto 'ridurre' nudo! ma quando 'start = None' non' myreduce ((lambda x, y: x + y), [1,2,3,4]) 'restituisce 11 ma dovrebbe avere 10; Ho preso 'sum' come' func' – SIslam

+0

Penso che la correzione dovrebbe apparire come 'per x in iterable [1:]:' – SIslam

+0

Il ciclo for dovrebbe scorrere su 'it', non' iterable': 'per x in esso:' – vaerek

0

Si potrebbe anche implementare fattoriale usando ridurre.

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

Le altre risposte sono fantastiche. Io semplicemente aggiungo un esempio illustrato che trovo abbastanza bene da capire reduce():

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

sarà calcolata come segue:

enter image description here

(Source) (mirror)

+1

Eccoci. Grazie! –

0

Ridurre esegue la funzione nel parametro # 1 successivamente attraverso i valori forniti dall'iteratore nel parametro #

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)