Ho visto riferimenti a funzioni curry in diversi articoli e blog ma non riesco a trovare una buona spiegazione (o almeno una che abbia senso!)
add x y = x+y
(curry) è diverso da add (x, y)=x+y
(non fretta)
Ho visto riferimenti a funzioni curry in diversi articoli e blog ma non riesco a trovare una buona spiegazione (o almeno una che abbia senso!)
add x y = x+y
(curry) è diverso da add (x, y)=x+y
(non fretta)
Risposte:
Il curriculum è quando si scompone una funzione che accetta più argomenti in una serie di funzioni che prendono ciascuno solo un argomento. Ecco un esempio in JavaScript:
function add (a, b) {
return a + b;
}
add(3, 4); // returns 7
Questa è una funzione che accetta due argomenti, aeb, e restituisce la loro somma. Ora curry questa funzione:
function add (a) {
return function (b) {
return a + b;
}
}
Questa è una funzione che accetta un argomento, a, e restituisce una funzione che accetta un altro argomento, b, e quella funzione restituisce la loro somma.
add(3)(4);
var add3 = add(3);
add3(4);
La prima istruzione restituisce 7, come l'istruzione add (3, 4). La seconda istruzione definisce una nuova funzione chiamata add3 che aggiungerà 3 al suo argomento. Questo è ciò che alcune persone possono chiamare una chiusura. La terza istruzione utilizza l'operazione add3 per aggiungere da 3 a 4, producendo di nuovo 7 come risultato.
[1, 2, 3, 4, 5]
che desideri moltiplicare per un numero arbitrario. In Haskell, posso scrivere map (* 5) [1, 2, 3, 4, 5]
per moltiplicare l'intero elenco 5
e quindi generare l'elenco [5, 10, 15, 20, 25]
.
map
deve essere una funzione che accetta solo 1 argomento - un elemento dall'elenco. La moltiplicazione - come concetto matematico - è un'operazione binaria; ci vogliono 2 argomenti. Tuttavia, in Haskell *
c'è una funzione curry, simile alla seconda versione di add
questa risposta. Il risultato di (* 5)
è una funzione che accetta un singolo argomento e lo moltiplica per 5 e che ci consente di usarlo con la mappa.
In un'algebra di funzioni, trattare con funzioni che accettano più argomenti (o un argomento equivalente che è una N-tupla) è in qualche modo inelegante - ma, come ha dimostrato Moses Schönfinkel (e, indipendentemente, Haskell Curry), non è necessario: tutto necessità sono funzioni che accettano un argomento.
Quindi, come gestisci qualcosa che esprimeresti naturalmente come, diciamo f(x,y)
,? Bene, lo consideri equivalente a f(x)(y)
- f(x)
, chiamalo g
, è una funzione e applichi quella funzione a y
. In altre parole, hai solo funzioni che accettano un argomento - ma alcune di quelle funzioni restituiscono altre funzioni (che accettano ANCHE un argomento ;-).
Come al solito, Wikipedia ha una bella voce di riepilogo su questo, con molti suggerimenti utili (probabilmente inclusi quelli riguardanti le tue lingue preferite ;-) e un trattamento matematico leggermente più rigoroso.
div :: Integral a => a -> a -> a
, noti quelle frecce multiple? "Mappa a per mappare una a a" è una lettura ;-). Si potrebbe utilizzare una (sola) argomento tupla per div
& C, ma che sarebbe davvero anti-idiomatica in Haskell.
Ecco un esempio concreto:
Supponiamo di avere una funzione che calcola la forza gravitazionale che agisce su un oggetto. Se non conosci la formula, puoi trovarla qui . Questa funzione accetta i tre parametri necessari come argomenti.
Ora, essendo sulla terra, vuoi solo calcolare le forze per gli oggetti su questo pianeta. In un linguaggio funzionale, potresti passare la massa della terra alla funzione e quindi valutarla parzialmente. Ciò che otterresti è un'altra funzione che accetta solo due argomenti e calcola la forza gravitazionale degli oggetti sulla terra. Questo si chiama curry.
Currying è una trasformazione che può essere applicata alle funzioni per consentire loro di accettare un argomento in meno rispetto al passato.
Ad esempio, in F # è possibile definire una funzione in questo modo: -
let f x y z = x + y + z
Qui la funzione f prende i parametri x, y e z e li somma insieme: -
f 1 2 3
Restituisce 6.
Dalla nostra definizione possiamo quindi definire la funzione curry per f: -
let curry f = fun x -> f x
Dove 'fun x -> fx' è una funzione lambda equivalente a x => f (x) in C #. Questa funzione immette la funzione che si desidera curry e restituisce una funzione che accetta un singolo argomento e restituisce la funzione specificata con il primo argomento impostato sull'argomento input.
Usando il nostro esempio precedente possiamo ottenere un curry di f così: -
let curryf = curry f
Possiamo quindi fare quanto segue: -
let f1 = curryf 1
Il che ci fornisce una funzione f1 che è equivalente a f1 yz = 1 + y + z. Ciò significa che possiamo fare quanto segue: -
f1 2 3
Che restituisce 6.
Questo processo è spesso confuso con 'applicazione di funzione parziale' che può essere definita in questo modo: -
let papply f x = f x
Sebbene possiamo estenderlo a più di un parametro, ovvero: -
let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.
Un'applicazione parziale prenderà la funzione e i parametri e restituirà una funzione che richiede uno o più parametri e, come mostrano i due esempi precedenti, viene implementata direttamente nella definizione della funzione F # standard in modo da poter ottenere così il risultato precedente: -
let f1 = f 1
f1 2 3
Che restituirà un risultato di 6.
In conclusione:-
La differenza tra l'applicazione del curry e l'applicazione della funzione parziale è che: -
Currying accetta una funzione e fornisce una nuova funzione accettando un singolo argomento e restituendo la funzione specificata con il suo primo argomento impostato su quell'argomento. Questo ci consente di rappresentare funzioni con più parametri come una serie di funzioni a argomento singolo . Esempio:-
let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6
L'applicazione di funzione parziale è più diretta: accetta una funzione e uno o più argomenti e restituisce una funzione con i primi n argomenti impostati sugli n argomenti specificati. Esempio:-
let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
Può essere un modo per utilizzare le funzioni per creare altre funzioni.
In javascript:
let add = function(x){
return function(y){
return x + y
};
};
Ci permetterebbe di chiamarlo così:
let addTen = add(10);
Quando viene eseguito, 10
viene passato come x
;
let add = function(10){
return function(y){
return 10 + y
};
};
il che significa che ci viene restituita questa funzione:
function(y) { return 10 + y };
Quindi quando chiami
addTen();
stai davvero chiamando:
function(y) { return 10 + y };
Quindi se lo fai:
addTen(4)
è lo stesso di:
function(4) { return 10 + 4} // 14
Quindi aggiungiamo addTen()
sempre dieci a qualunque cosa passiamo. Possiamo fare funzioni simili allo stesso modo:
let addTwo = add(2) // addTwo(); will add two to whatever you pass in
let addSeventy = add(70) // ... and so on...
Ora l'ovvia domanda di follow-up è perché mai vorresti mai farlo? Trasforma quella che era un'operazione desiderosa x + y
in una che può essere superata pigramente, il che significa che possiamo fare almeno due cose: 1. cache operazioni costose 2. raggiungere astrazioni nel paradigma funzionale.
Immagina che la nostra funzione al curry fosse così:
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
Potremmo chiamare questa funzione una volta, quindi fare il giro del risultato per usarlo in molti posti, il che significa che facciamo le cose costose dal punto di vista computazionale solo una volta:
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
Possiamo ottenere astrazioni in modo simile.
Una funzione curry è una funzione di diversi argomenti riscritti in modo tale che accetta il primo argomento e restituisce una funzione che accetta il secondo argomento e così via. Ciò consente alle funzioni di diversi argomenti di applicare parzialmente alcuni dei loro argomenti iniziali.
map
una funzione f
su un elenco di elenchi, xss
è possibile farlo map (map f) xss
.
Ecco un esempio di giocattolo in Python:
>>> from functools import partial as curry
>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
print who, 'said regarding', subject + ':'
print '"' + quote + '"'
>>> display_quote("hoohoo", "functional languages",
"I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."
>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")
>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."
(Basta usare la concatenazione tramite + per evitare distrazioni per i programmatori non Python.)
Modifica per aggiungere:
Vedi http://docs.python.org/library/functools.html?highlight=partial#functools.partial , che mostra anche la distinzione tra oggetto parziale e funzione nel modo in cui Python lo implementa.
Currying sta traducendo una funzione da richiamabile f(a, b, c)
a richiamabile come f(a)(b)(c)
.
Altrimenti il curry avviene quando si scompone una funzione che accetta più argomenti in una serie di funzioni che prendono parte degli argomenti.
Letteralmente, il curry è una trasformazione di funzioni: da un modo di chiamare ad un altro. In JavaScript, di solito realizziamo un wrapper per mantenere la funzione originale.
Currying non chiama una funzione. Lo trasforma e basta.
Facciamo funzione curry che esegue il curry per funzioni a due argomenti. In altre parole, curry(f)
per due argomenti lo f(a, b)
traduce inf(a)(b)
function curry(f) { // curry(f) does the currying transform
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
Come puoi vedere, l'implementazione è una serie di wrapper.
curry(func)
è un wrapper function(a)
.sum(1)
, l'argomento viene salvato nell'ambiente lessicale e viene restituito un nuovo wrapper function(b)
.sum(1)(2)
chiama infine function(b)
fornendo 2 e passa la chiamata alla somma multi-argomento originale.Se capisci partial
che sei a metà strada. L'idea di partial
è preapplicare gli argomenti a una funzione e restituire una nuova funzione che vuole solo gli argomenti rimanenti. Quando viene chiamata questa nuova funzione, include gli argomenti precaricati insieme a tutti gli argomenti forniti.
In Clojure +
è una funzione ma per rendere le cose nettamente chiare:
(defn add [a b] (+ a b))
Potresti essere consapevole che la inc
funzione aggiunge semplicemente 1 a qualsiasi numero sia passato.
(inc 7) # => 8
Costruiamolo noi stessi usando partial
:
(def inc (partial add 1))
Qui restituiamo un'altra funzione che ha 1 caricato nel primo argomento di add
. Dato che add
accetta due argomenti, la nuova inc
funzione vuole solo l' b
argomento, non 2 argomenti come prima poiché 1 è già stato parzialmente applicato. Quindi partial
è uno strumento dal quale creare nuove funzioni con valori predefiniti presupposti. Ecco perché in un linguaggio funzionale le funzioni spesso ordinano argomenti da generali a specifici. Ciò semplifica il riutilizzo di tali funzioni da cui costruire altre funzioni.
Ora immagina se il linguaggio fosse abbastanza intelligente da capire introspettivamente che add
voleva due argomenti. Quando gli abbiamo passato un argomento, piuttosto che l'espressione, se la funzione applicasse parzialmente l'argomento, lo abbiamo passato per nostro conto, capendo che probabilmente intendevamo fornire l'altro argomento in seguito? Potremmo quindi definire inc
senza usare esplicitamente partial
.
(def inc (add 1)) #partial is implied
Questo è il modo in cui alcune lingue si comportano. È eccezionalmente utile quando si desidera comporre le funzioni in trasformazioni più grandi. Ciò porterebbe a trasduttori.
Ho trovato utile questo articolo e l'articolo a cui fa riferimento per comprendere meglio il curry: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
Come altri hanno già detto, è solo un modo per avere una funzione con un parametro.
Questo è utile in quanto non devi assumere quanti parametri verranno passati, quindi non hai bisogno di un parametro 2, 3 parametri e 4 parametri.
Come tutte le altre risposte, il curry aiuta a creare funzioni parzialmente applicate. Javascript non fornisce supporto nativo per il curry automatico. Pertanto, gli esempi forniti sopra potrebbero non essere utili nella codifica pratica. C'è un ottimo esempio in livescript (che essenzialmente si compila in js) http://livescript.net/
times = (x, y) --> x * y
times 2, 3 #=> 6 (normal use works as expected)
double = times 2
double 5 #=> 10
Nell'esempio sopra quando hai dato meno no di argomenti livescript genera una nuova funzione al curry per te (doppio)
Curry può semplificare il tuo codice. Questo è uno dei motivi principali per utilizzare questo. Currying è un processo di conversione di una funzione che accetta n argomenti in n funzioni che accettano solo un argomento.
Il principio è passare gli argomenti della funzione passata, usando la proprietà di chiusura (chiusura), per memorizzarli in un'altra funzione e trattarla come un valore di ritorno, e queste funzioni formano una catena, e gli argomenti finali vengono passati per completare l'operazione.
Il vantaggio è che può semplificare l'elaborazione dei parametri trattando un parametro alla volta, il che può anche migliorare la flessibilità e la leggibilità del programma. Questo rende anche il programma più gestibile. Dividere anche il codice in pezzi più piccoli lo renderebbe riutilizzabile.
Per esempio:
function curryMinus(x)
{
return function(y)
{
return x - y;
}
}
var minus5 = curryMinus(1);
minus5(3);
minus5(5);
Posso anche fare ...
var minus7 = curryMinus(7);
minus7(3);
minus7(5);
Questo è ottimo per rendere il codice complesso pulito e per la gestione di metodi non sincronizzati ecc.
Una funzione curry viene applicata a più elenchi di argomenti, anziché a uno solo.
Ecco una normale funzione non curry, che aggiunge due parametri Int, xey:
scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3
Ecco una funzione simile che è al curry. Invece di un elenco di due parametri Int, si applica questa funzione a due elenchi di un parametro Int ciascuno:
scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3
Quello che sta succedendo qui è che quando invochi curriedSum
, ricevi effettivamente due invocazioni di funzioni tradizionali. La prima chiamata di funzione accetta un singolo parametro Int denominato x
e restituisce un valore di funzione per la seconda funzione. Questa seconda funzione accetta il parametro Int
y
.
Ecco una funzione chiamata first
che fa nello spirito ciò che curriedSum
farebbe la prima invocazione della funzione tradizionale :
scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int
Applicando 1 alla prima funzione - in altre parole, invocando la prima funzione e passando in 1 - si ottiene la seconda funzione:
scala> val second = first(1)
second: (Int) => Int = <function1>
Applicando 2 alla seconda funzione si ottiene il risultato:
scala> second(2)
res6: Int = 3
Un esempio di curry sarebbe quando disponi di funzioni che conosci solo uno dei parametri al momento:
Per esempio:
func aFunction(str: String) {
let callback = callback(str) // signature now is `NSData -> ()`
performAsyncRequest(callback)
}
func callback(str: String, data: NSData) {
// Callback code
}
func performAsyncRequest(callback: NSData -> ()) {
// Async code that will call callback with NSData as parameter
}
Qui, poiché non si conosce il secondo parametro per il callback durante l'invio, performAsyncRequest(_:)
è necessario creare un altro lambda / chiusura per inviarlo alla funzione.
func callback
tornando da solo? Si chiama @ callback(str)
così let callback = callback(str)
, callback è solo il valore di ritorno difunc callback
func callback(_:data:)
accetta due parametri, qui ne do solo uno, il String
, quindi è in attesa del prossimo ( NSData
), ecco perché ora let callback
è un'altra funzione in attesa del passaggio dei dati
Ecco l'esempio della versione generica e la più breve per la funzione currying con n no. di params.
const add = a => b => b ? add(a + b) : a;
const add = a => b => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)());
Qui puoi trovare una semplice spiegazione dell'implementazione del curry in C #. Nei commenti, ho provato a mostrare come il curry può essere utile:
public static class FuncExtensions {
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x1 => x2 => func(x1, x2);
}
}
//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);
//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times
//with different input parameters.
int result = func(1);
Currying è una delle funzioni di ordine superiore di Java Script.
Currying è una funzione di molti argomenti che viene riscritta in modo tale da prendere il primo argomento e restituire una funzione che a sua volta utilizza gli argomenti rimanenti e restituisce il valore.
Confuso?
Vediamo un esempio,
function add(a,b)
{
return a+b;
}
add(5,6);
Questo è simile alla seguente funzione di curry,
function add(a)
{
return function(b){
return a+b;
}
}
var curryAdd = add(5);
curryAdd(6);
Cosa significa questo codice?
Ora leggi di nuovo la definizione,
Currying è una funzione di molti argomenti che viene riscritta in modo tale da prendere il primo argomento e restituire una funzione che a sua volta utilizza gli argomenti rimanenti e restituisce il valore.
Ancora confuso? Lasciami spiegare in profondità!
Quando chiami questa funzione,
var curryAdd = add(5);
Ti restituirà una funzione come questa,
curryAdd=function(y){return 5+y;}
Quindi, questo si chiama funzioni di ordine superiore. Significato, invocare una funzione a turno restituisce un'altra funzione è una definizione esatta per la funzione di ordine superiore. Questo è il più grande vantaggio per la leggenda, Java Script. Quindi torna al curry,
Questa riga passerà il secondo argomento alla funzione curryAdd.
curryAdd(6);
che a sua volta risulta,
curryAdd=function(6){return 5+6;}
// Which results in 11
Spero che tu capisca l'uso del curry qui. Quindi, venendo ai vantaggi,
Perché Currying?
Utilizza la riusabilità del codice. Meno codice, meno errore. Potresti chiedere come è meno codice?
Posso provarlo con lo script ECMA 6 nuove funzioni freccia funzioni.
Sì! ECMA 6, forniscici la meravigliosa funzione chiamata funzioni freccia,
function add(a)
{
return function(b){
return a+b;
}
}
Con l'aiuto della funzione freccia, possiamo scrivere la funzione sopra come segue,
x=>y=>x+y
Va bene vero?
Quindi, meno codice e meno bug !!
Con l'aiuto di queste funzioni di ordine superiore si può facilmente sviluppare un codice privo di bug.
Ti sfido!
Spero, hai capito cosa sta succedendo. Non esitate a commentare qui se avete bisogno di chiarimenti.
Grazie buona giornata!
C'è un esempio di "Currying in ReasonML".
let run = () => {
Js.log("Curryed function: ");
let sum = (x, y) => x + y;
Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
let per2 = sum(2);
Printf.printf("per2(3) : %d\n", per2(3));
};
curry
euncurry
di Haskell. Ciò che è importante qui è che questi isomorfismi sono fissati in anticipo, e quindi "integrati" nella lingua.