2012-07-21 12 views
5

Mi è stato chiesto qual è la relazione tra applicazione funzione parziale e chiusure. Direi che non ce n'è, a meno che non mi manchi il punto. Diciamo che sto scrivendo in python e ho una funzione molto semplice MySum definiti come segue:Applicazione parziale e chiusure

MySum = lambda x, y : x + y; 

Ora sto fissare un parametro per ottenere una funzione con i più piccoli arity che restituisce lo stesso valore che avrebbe MySum di ritorno, se ho chiamato con gli stessi parametri (applicazione parziale):

MyPartialSum = lambda x : MySum(x, 0); 

ho potuto fare la stessa stessa cosa con C:

int MySum(int x, int y) { return x + y; } 
int MyPartialSum(int x) { return MySum(x, 0); } 

Così, il du mb domanda è: qual è la differenza? Perché dovrei aver bisogno di chiusure per applicazioni parziali? Questi codici sono equivalenti, non vedo cosa sia legato alle chiusure e alle applicazioni parziali.

risposta

3

L'applicazione parziale è una tecnica con cui si prende una funzione esistente e un sottoinsieme di argomenti e si genera una nuova funzione che accetta gli argomenti rimanenti.

In altre parole, se la funzionalità dei F(a, b), una funzione che si applica applicazione parziale di a assomiglierebbe B(fn, a) dove F(a, b) = B(F, a)(b).

Nel tuo esempio stai semplicemente creando nuove funzioni, piuttosto che applicare un'applicazione parziale a quella esistente.

Ecco un esempio in python:

def curry_first(fn, arg): 
    def foo(*args): 
     return fn(arg, *args) 
    return foo 

Questo crea una chiusura sopra la funzione in dotazione e l'argomento. Viene restituita una nuova funzione che chiama la prima funzione con la nuova firma di argomento. La chiusura è importante: consente l'accesso a fn a arg. Ora si può fare questo genere di cose:

add = lambda x, y : x + y; 
add2 = curry_first(add, 2) 
add2(4) # returns 6 

ho sentito questo di solito indicato come currying.

+0

Ho provato a chiarire le cose leggendo molte cose sull'argomento: c'è molta confusione tra curry e applicazioni parziali, che sono comunemente scambiate ma sono cose diverse. Wikipedia dice: "l'applicazione parziale (o l'applicazione di una funzione parziale) si riferisce al processo di fissare un numero di argomenti a una funzione, producendo un'altra funzione di arità più piccola" – Cancer

+0

Che è esattamente ciò che sta accadendo sopra. Per quanto ne so, il curry è solo un'espressione particolare della forma. – Hamish

+0

Sì, capisco, il tuo codice lo fa, ed è abbastanza chiaro. Ma ... non lo faccio anche io? Diversamente, è ovvio, non sto passando funzioni come parametri, ma sto correggendo anche un argomento e creando una funzione con arità più piccola che fa la stessa cosa e restituisce lo stesso valore come se avessi chiamato la funzione originale con tutti i parametri . Scusate, mi sento piuttosto stupido, sono perplesso ... – Cancer

0

Per me, utilizzando il partialSum in questo modo, si assicura che si dipenda solo da una funzione per sommare i numeri (MySum) e che renderà il debug molto più semplice se le cose vanno male, perché non dovresti preoccuparti sulla logica del tuo codice in due parti diverse del tuo codice.

Se in futuro si decide di cambiare la logica del MySum, (diciamo per esempio, fanno tornare x + y + 1), allora non dovrete preoccuparvi di MyPartialSum perché richiede MySum

Anche se sembra stupido, avere il codice scritto in questo modo serve solo a semplificare il processo delle dipendenze nelle funzioni. Sono sicuro che lo noterai più avanti nei tuoi studi.

+0

Vedo. Ad ogni modo, potrei farlo indipendentemente dalla lingua che sto usando per supportare chiusure o meno. Le chiusure sono piuttosto inutili in questo caso – Cancer

+0

Questo è solo "codice di scrittura". L'applicazione parziale dovrebbe restituire una * funzione *, non un risultato. – Hamish

+0

Dovrebbe? È molto interessante! Quindi, se ho definito: MyPartialSum = lambda x: lambda x: MySum (x, 0) che sarebbe un'applicazione "vera" parziale? Questo non mi è molto chiaro – Cancer

1

applicazione funzione parziale è circa fissa alcuni argomenti di una data funzione per produrre un'altra funzione con meno argomenti, come

sum = lambda x, y: x + y 
inc = lambda x: sum(x, 1) 

noti che 'inc' è 'somma' parzialmente applicato, senza catturare nulla dal il contesto (come hai detto la chiusura).

Ma tali funzioni scritte a mano (di solito anonime) sono un po 'noiose. Si può usare una fabbrica di funzioni, che restituisce una funzione interna. La funzione interna può essere parametrizzato catturando qualche variabile dal suo contesto, come

# sum = lambda x, y: x + y 
def makePartialSumF(n): 
    def partialSumF(x): 
     return sum(x, n) 
    return partialSumF 

inc = makePartialSumF(1) 
plusTwo = makePartialSumF(2) 

Ecco la fabbrica makePartialSumF viene richiamato due volte. Ogni chiamata ha come risultato una funzione parzialeSumF (acquisizione di valori diversi come n). L'uso della chiusura rende conveniente l'implementazione dell'applicazione parziale. Quindi puoi dire che l'applicazione parziale può essere implementata tramite la chiusura. Ovviamente le chiusure possono fare molte altre cose! (Come nodo laterale, pitone non ha corretta chiusura.)

Currying è di trasformare una funzione di N argomenti in una funzione unaria che restituisce una funzione unaria ... per esempio abbiamo una funzione che prende tre argomenti e restituisce un valore:

sum = lambda x, y, z: x + y + z 

La versione al curry è

curriedSum = lambda x: lambda y: lambda z: x + y + z 

Scommetto che non sarebbe scrivere codice python così. IMO la motivazione di Currying è per lo più di interesse teorico. (Una struttura per esprimere calcoli usando solo funzioni unarie: ogni funzione è unaria!) Il sottoprodotto pratico è che, nelle lingue in cui le funzioni sono curate, alcune applicazioni parziali (quando si "aggiustano" argomenti da sinistra) sono banali come fornendo argomenti alla funzione curry. (Ma non tutte le applicazioni parziali sono come tali Esempio: dato f (x, y, z) = x + 2 * y + 3 * z, quando si lega y a una costante per ottenere una funzione di due variabili.) Quindi tu Può dire, Currying è una tecnica che, in pratica e come sottoprodotto, può rendere banali molte utili applicazioni parziali funzionali, ma non è questo il punto di Currying.

1

Semplicemente, il risultato di un'applicazione parziale è normalmente implementato come chiusura.

1

Le chiusure non sono una funzionalità richiesta in una lingua. Sto sperimentando un linguaggio fatto in casa, lambdatalk, in cui lambdas non crea chiusure ma accetta un'applicazione parziale. Per esempio questo è come l'insieme [contro, auto, CDR] potrebbe essere definita in SCHEMA:

(def cons (lambda (x y) (lambda (m) (m x y)))) 
(def car (lambda (z) (z (lambda (p q) p)))) 
(def cdr (lambda (z) (z (lambda (p q) q)))) 

(car (cons 12 34)) -> 12 
(cdr (cons 12 34)) -> 34 

e in lambdatalk:

{def cons {lambda {:x :y :m} {:m :x :y}}} 
{def car {lambda {:z} {:z {lambda {:x :y} :x}}}} 
{def cdr {lambda {:z} {:z {lambda {:x :y} :y}}}} 

{car {cons 12 34}} -> 12 
{cdr {cons 12 34}} -> 34 

nello Schema lambda esterno salva xey in una chiusura l'interno lambda può accedere dato m. In lambdatalk il lambda salva: xe: ye restituisce un nuovo lambda in attesa di: m. Quindi, anche se la chiusura (e lo scope lessicale) sono funzionalità utili, non c'è una necessità. Senza variabili libere, fuori da ogni ambito lessicale, le funzioni sono vere caselle nere senza alcun effetto collaterale, in una totale indipendenza, seguendo un vero paradigma funzionale. Non la pensi così?