Domanda

Vedo spesso su Internet varie lamentele secondo cui gli esempi di curry di altre persone non sono curry, ma in realtà sono solo un'applicazione parziale.

Non ho trovato una spiegazione decente di cosa sia l'applicazione parziale o come differisce dal curry. Sembra esserci una confusione generale, con esempi equivalenti descritti come curry in alcuni punti e applicazione parziale in altri.

Qualcuno potrebbe fornirmi una definizione di entrambi i termini e dettagli su come differiscono?

È stato utile?

Soluzione

Currying sta convertendo una singola funzione di n in n funzioni con un singolo argomento ciascuno. Data la seguente funzione:

function f(x,y,z) { z(x(y));}

Quando è al curry, diventa:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

Per ottenere l'applicazione completa di f (x, y, z), devi farlo:

f(x)(y)(z);

Molte lingue funzionali ti consentono di scrivere f x y z . Se chiami solo fxy o f (x) (y) , otterrai una funzione parzialmente applicata: il valore restituito è una chiusura di lambda (z) {z (x (y))} con i valori passati di xey a f (x, y) .

Un modo per utilizzare un'applicazione parziale è definire le funzioni come applicazioni parziali di funzioni generalizzate, come piega :

function fold(combineFunction, accumulator, list) {/* ... */}
function sum     = curry(fold)(lambda(accum,e){e+accum}))(0);
function length  = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list)  //returns 10

Altri suggerimenti

Il modo più semplice per vedere in che modo differiscono è prendere in considerazione un esempio reale . Supponiamo di avere una funzione Aggiungi che accetta 2 numeri come input e restituisce un numero come output, ad es. Aggiungi (7, 5) restituisce 12 . In questo caso:

  • L'applicazione parziale della funzione Aggiungi con un valore 7 ci darà una nuova funzione come output. La stessa funzione accetta 1 numero come input e genera un numero. Come tale:

    Partial(Add, 7); // returns a function f2 as output
    
                     // f2 takes 1 number as input and returns a number as output
    

    Quindi possiamo farlo:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
    
  • Currying la funzione Aggiungi ci darà una nuova funzione come output. La stessa funzione prende 1 numero come input e produce ancora un'altra nuova funzione. La terza funzione accetta quindi 1 numero come input e restituisce un numero come output. Come tale:

    Curry(Add); // returns a function f2 as output
    
                // f2 takes 1 number as input and returns a function f3 as output
                // i.e. f2(number) = f3
    
                // f3 takes 1 number as input and returns a number as output
                // i.e. f3(number) = number
    

    Quindi possiamo farlo:

    f2 = Curry(Add);
    f3 = f2(7);
    f3(5); // returns 12
    

In altre parole, "quotare" " e "applicazione parziale" sono due funzioni totalmente diverse. Currying richiede esattamente 1 input, mentre l'applicazione parziale richiede 2 (o più) input.

Anche se entrambi restituiscono una funzione come output, le funzioni restituite hanno forme totalmente diverse come dimostrato sopra.

Nota: questo è stato tratto da Nozioni di base di F # un eccellente articolo introduttivo per gli sviluppatori .NET che ottengono nella programmazione funzionale.

  

Currying significa spezzare una funzione con molti argomenti in una serie   di funzioni che ciascuna prende un argomento e alla fine produce il   stesso risultato della funzione originale. Currying è probabilmente il più   argomento stimolante per gli sviluppatori che non conoscono la programmazione funzionale, soprattutto perché   viene spesso confuso con un'applicazione parziale. Puoi vederli entrambi al lavoro   in questo esempio:

let multiply x y = x * y    
let double = multiply 2
let ten = double 5
     

Immediatamente, dovresti vedere un comportamento diverso dalla maggior parte   lingue imperative. La seconda istruzione crea una nuova funzione   chiamato double passando un argomento a una funzione che ne accetta due.   Il risultato è una funzione che accetta un argomento int e produce il   stesso output come se avessi chiamato moltiplicare per x uguale a 2 e y   uguale a tale argomento. In termini di comportamento, è lo stesso di questo   codice:

let double2 z = multiply 2 z
     

Spesso, la gente dice erroneamente che il moltiplicarsi è fatto in modo da formare il doppio.   Ma questo è solo un po 'vero. La funzione di moltiplicazione è curry, ma   ciò accade quando è definito perché le funzioni in F # sono trascinate da   predefinito. Quando viene creata la doppia funzione, è più preciso   dire che la funzione moltiplica è parzialmente applicata.

     

La funzione moltiplica è in realtà una serie di due funzioni. Il primo   La funzione accetta un argomento int e restituisce un'altra funzione,   legando efficacemente x ad un valore specifico. Questa funzione accetta anche   un argomento int che puoi considerare come il valore da associare a y. Dopo   chiamando questa seconda funzione, xey sono entrambi associati, quindi il risultato è   il prodotto di xey come definito nel corpo del doppio.

     

Per creare double, la prima funzione nella catena di moltiplicare   le funzioni sono valutate per applicare parzialmente moltiplicare. Il risultato   alla funzione viene assegnato il nome doppio. Quando viene valutato double, utilizza   il suo argomento insieme al valore parzialmente applicato per creare il   risultato.

Domanda interessante. Dopo un po 'di ricerca, " L'applicazione funzione parziale non sta valutando " ha dato la migliore spiegazione Ho trovato. Non posso dire che la differenza pratica sia particolarmente ovvia per me, ma poi non sono un esperto di FP ...

Un'altra pagina utile (che confesso di non aver ancora letto del tutto) è " Currying e applicazione parziale con chiusure Java " .

Sembra che questa sia una coppia di termini ampiamente confusi, intendiamoci.

Ho risposto in un'altra discussione https://stackoverflow.com/a/12846865/1685865 . In breve, l'applicazione di funzioni parziali riguarda la correzione di alcuni argomenti di una determinata funzione multivariabile per produrre un'altra funzione con meno argomenti, mentre Currying consiste nel trasformare una funzione di N argomenti in una funzione unaria che restituisce una funzione unaria ... [Un esempio di Currying è mostrato alla fine di questo post.]

Currying è per lo più di interesse teorico: si possono esprimere calcoli usando solo funzioni unarie (cioè ogni è unaria). In pratica e come sottoprodotto, è una tecnica che può rendere banali molte applicazioni funzionali parziali (ma non tutte) se il linguaggio ha funzioni curry. Ancora una volta, non è l'unico mezzo per implementare applicazioni parziali. Quindi potresti incontrare scenari in cui l'applicazione parziale viene eseguita in altro modo, ma le persone lo stanno scambiando come Currying.

(Esempio di curriculum)

In pratica non si scriverebbe semplicemente

lambda x: lambda y: lambda z: x + y + z

o javascript equivalente

function (x) { return function (y){ return function (z){ return x + y + z }}}

anziché

lambda x, y, z: x + y + z

per il bene di Currying.

Currying è una funzione di uno argomento che accetta una funzione f e restituisce una nuova funzione h . Nota che h prende un argomento da X e restituisce una funzione che mappa Y su Z :

curry(f) = h 
f: (X x Y) -> Z 
h: X -> (Y -> Z)

L'applicazione parziale è una funzione di due (o più) argomenti che accettano una funzione f e uno o più argomenti aggiuntivi per f e restituisce una nuova funzione g :

part(f, 2) = g
f: (X x Y) -> Z 
g: Y -> Z

La confusione sorge perché con una funzione a due argomenti vale la seguente uguaglianza:

partial(f, a) = curry(f)(a)

Entrambe le parti produrranno la stessa funzione a argomento singolo.

L'uguaglianza non è vera per le funzioni di arity più elevate perché in questo caso il curry restituirà una funzione a argomento singolo, mentre l'applicazione parziale restituirà una funzione a argomento multiplo.

La differenza sta anche nel comportamento, mentre il curry trasforma in modo ricorsivo l'intera funzione originale (una volta per ogni argomento), l'applicazione parziale è solo una sostituzione di un passaggio.

Fonte: Wikipedia Currying .

La differenza tra curry e l'applicazione parziale può essere meglio illustrata attraverso questo esempio JavaScript seguente:

function f(x, y, z) {
    return x + y + z;
}

var partial = f.bind(null, 1);

6 === partial(2, 3);

L'applicazione parziale risulta in una funzione di minore arità; nell'esempio sopra, f ha un'arità di 3 mentre parziale ha solo un'arità di 2. Ancora più importante, una funzione parzialmente applicata restituirebbe immediatamente il risultato dopo essere stato invocato , non un'altra funzione nella catena del curry. Quindi se stai vedendo qualcosa come partial (2) (3) , non è un'applicazione parziale in realtà.

Ulteriori letture:

Per me l'applicazione parziale deve creare una nuova funzione in cui gli argomenti utilizzati sono completamente integrati nella funzione risultante.

La maggior parte dei linguaggi funzionali implementa il curry restituendo una chiusura: non valutare sotto lambda quando applicato parzialmente. Quindi, affinché l'applicazione parziale sia interessante, dobbiamo fare la differenza tra curry e applicazione parziale e considerare l'applicazione parziale come curry più valutazione sotto lambda.

Potrei sbagliarmi qui, dato che non ho un forte background in matematica teorica o programmazione funzionale, ma dalla mia breve incursione in FP, sembra che il curry tende a trasformare una funzione di N argomenti in N funzioni di un argomento, mentre l'applicazione parziale [in pratica] funziona meglio con le funzioni variadiche con un numero indeterminato di argomenti. Conosco alcuni degli esempi nelle risposte precedenti che sfidano questa spiegazione, ma mi ha aiutato di più a separare i concetti. Prendi in considerazione questo esempio (scritto in CoffeeScript per succinto, mi scuso se confonde ulteriormente, ma per favore chiedi chiarimenti, se necessario):

# partial application
partial_apply = (func) ->
  args = [].slice.call arguments, 1
  -> func.apply null, args.concat [].slice.call arguments

sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num

add_to_7_and_5 = partial_apply sum_variadic, 7, 5

add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45

# currying
curry = (func) ->
  num_args = func.length
  helper = (prev) ->
    ->
      args = prev.concat [].slice.call arguments
      return if args.length < num_args then helper args else func.apply null, args
  helper []

sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15

Questo è ovviamente un esempio inventato, ma si noti che l'applicazione parziale di una funzione che accetta un numero qualsiasi di argomenti ci consente di eseguire una funzione ma con alcuni dati preliminari. La modifica di una funzione è simile ma ci consente di eseguire una funzione di parametro N in pezzi fino a quando, ma solo fino a quando, tutti i parametri N vengono considerati.

Ancora una volta, questa è la mia opinione sulle cose che ho letto. Se qualcuno non è d'accordo, apprezzerei un commento sul perché piuttosto che un downvote immediato. Inoltre, se CoffeeScript è difficile da leggere, visitare coffeescript.org, fare clic su " provare coffeescript " e incolla il mio codice per vedere la versione compilata, che potrebbe (si spera) avere più senso. Grazie!

Ho avuto questa domanda molto durante l'apprendimento e da allora mi è stata posta molte volte. Il modo più semplice di descrivere la differenza è che entrambi sono uguali :) Mi spiego ... ci sono ovviamente differenze.

Sia l'applicazione parziale che il curry implicano fornire argomenti a una funzione, forse non tutti in una volta. Un esempio abbastanza canonico è l'aggiunta di due numeri. Nello pseudocodice (in realtà JS senza parole chiave), la funzione di base può essere la seguente:

add = (x, y) => x + y

Se volessi un " addOne " funzione, potrei applicarlo parzialmente o curry:

addOneC = curry(add, 1)
addOneP = partial(add, 1)

Ora usarli è chiaro:

addOneC(2) #=> 3
addOneP(2) #=> 3

Quindi qual è la differenza? Bene, è sottile, ma l'applicazione parziale comporta la fornitura di alcuni argomenti e la funzione restituita eseguirà la funzione principale alla prossima chiamata mentre il curry continuerà ad aspettare fino a quando non avrà tutti gli argomenti necessari:

curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want

partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error

In breve, utilizzare un'applicazione parziale per precompilare alcuni valori, sapendo che la prossima volta che si chiama il metodo, verrà eseguito, lasciando indefiniti tutti gli argomenti non forniti; utilizzare il curry quando si desidera restituire continuamente una funzione parzialmente applicata tutte le volte necessarie per soddisfare la firma della funzione. Un ultimo esempio inventato:

curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works

partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters

Spero che questo aiuti!

AGGIORNAMENTO: Alcune lingue o implementazioni lib ti permetteranno di passare un arity (numero totale di argomenti nella valutazione finale) all'implementazione parziale dell'applicazione che potrebbe confondere le mie due descrizioni in un pasticcio confuso ... ma a quel punto, il due tecniche sono ampiamente intercambiabili.

Risposta semplice

Curry: ti consente di chiamare una funzione, suddividendola in più chiamate, fornendo un argomento per chiamata.

Parziale: ti consente di chiamare una funzione, suddividendola in più chiamate, fornendo più argomenti per chiamata.


Suggerimenti semplici

Entrambi consentono di chiamare una funzione fornendo meno argomenti (o, meglio, fornendo cumulativamente). In realtà entrambi vincolano (ad ogni chiamata) un valore specifico a specifici argomenti della funzione.

La vera differenza può essere vista quando la funzione ha più di 2 argomenti.


Simple e (c) (sample)

(in Javascript)

function process(context, success_callback, error_callback, subject) {...}

perché passare sempre gli argomenti, come il contesto e i callback, se saranno sempre gli stessi? Associa solo alcuni valori per la funzione

processSubject = _.partial(process, my_context, my_success, my_error)

e chiamalo su subject1 e foobar con

processSubject('subject1');
processSubject('foobar');

Comodo, vero? ??

Con currying dovrai passare un argomento alla volta

curriedProcess = _.curry(process);
processWithBoundedContext = curriedProcess(my_context);
processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls

result1 = processWithCallbacks('subject1');
// same as: process(my_context, my_success, my_error, 'subject1');
result2 = processWithCallbacks('foobar'); 
// same as: process(my_context, my_success, my_error, 'foobar');

responsabilità

Ho saltato tutte le spiegazioni accademiche / matematiche. Perché non lo so. Forse ha aiutato ??

Ci sono altre grandi risposte qui, ma credo che questo esempio (secondo la mia comprensione) in Java potrebbe essere di beneficio ad alcune persone:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  A, Function< B, X >  > curry( BiFunction< A, B, X > bif ){
    return a -> partiallyApply( bif, a );
}

Quindi il curry ti offre una funzione a argomento singolo per creare funzioni, dove l'applicazione parziale crea una funzione wrapper che codifica in modo rigido uno o più argomenti.

Se vuoi copiare & incollare, quanto segue è più rumoroso ma più amichevole con cui lavorare poiché i tipi sono più indulgenti:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  ? super A,  Function< ? super B, ? extends X >  > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
    return a -> partiallyApply( bif, a );
}

Nello scrivere questo, ho confuso curry e non curry. Sono trasformazioni inverse sulle funzioni. In realtà non importa come si chiama, purché si ottenga ciò che rappresentano la trasformazione e il suo inverso.

Uncurrying non è definito molto chiaramente (o meglio, ci sono definizioni "contrastanti" che catturano tutte lo spirito dell'idea). Fondamentalmente, significa trasformare una funzione che accetta più argomenti in una funzione che accetta un singolo argomento. Ad esempio,

(+) :: Int -> Int -> Int

Ora, come si trasforma in una funzione che accetta un singolo argomento? Cheat, ovviamente!

plus :: (Int, Int) -> Int

Nota che ora plus prende un singolo argomento (che è composto da due cose). Super!

Qual è il punto di questo? Bene, se hai una funzione che accetta due argomenti e hai un paio di argomenti, è bello sapere che puoi applicare la funzione agli argomenti e ottenere comunque quello che ti aspetti. E, in effetti, l'idraulico per farlo esiste già, quindi non devi fare cose come la corrispondenza esplicita del modello. Tutto quello che devi fare è:

(uncurry (+)) (1,2)

Quindi cos'è un'applicazione con funzione parziale? È un modo diverso di trasformare una funzione in due argomenti in una funzione con un argomento. Funziona diversamente però. Ancora una volta, prendiamo (+) come esempio. Come possiamo trasformarlo in una funzione che accetta un singolo Int come argomento? Noi imbrogliamo!

((+) 0) :: Int -> Int

Questa è la funzione che aggiunge zero a qualsiasi Int.

((+) 1) :: Int -> Int

aggiunge 1 a qualsiasi Int. Ecc. In ciascuno di questi casi, (+) è "parzialmente applicato".

Suppongo che la maggior parte delle persone che pongono questa domanda abbiano già familiarità con i concetti di base, quindi non è necessario parlarne. È la sovrapposizione che è la parte confusa.

Potresti essere in grado di utilizzare appieno i concetti, ma li comprendi insieme come questa sfocatura concettuale amorfa pseudo-atomica. Ciò che manca è sapere dove si trova il confine tra di loro.

Invece di definire ciò che ciascuno è, è più facile evidenziare solo le loro differenze & # 8212; il confine.

Currying è quando definisci la funzione.

Applicazione parziale è quando chiamate la funzione.

Applicazione è un linguaggio matematico per chiamare una funzione.

L'applicazione parziale richiede di chiamare una funzione curry e ottenere una funzione come tipo di ritorno.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top