Domanda

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

È stato utile?

Soluzione

Il currying avviene quando si scompone una funzione che accetta più argomenti in una serie di funzioni che prendono parte degli argomenti.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 eseguiremo 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 alcuni potrebbero chiamare una chiusura.La terza istruzione utilizza l'operazione add3 per sommare 3 a 4, producendo nuovamente 7 come risultato.

Altri suggerimenti

In un'algebra di funzioni, gestire funzioni che accettano più argomenti (o un argomento equivalente che sia una N-tupla) è alquanto inelegante - ma, come ha dimostrato Moses Schönfinkel (e, indipendentemente, Haskell Curry), non è necessario:tutto ciò di cui hai bisogno sono funzioni che accettano un argomento.

Allora come gestisci qualcosa che esprimeresti naturalmente come, ad esempio, f(x,y)?Beh, lo consideri equivalente a f(x)(y) -- f(x), chiamalo g, è una funzione e tu applichi quella funzione a y.In altre parole, hai solo funzioni che accettano un argomento, ma alcune di queste funzioni restituiscono altre funzioni (che ANCHE accettano un argomento;-).

Come di solito, Wikipedia ha un bel riassunto sull'argomento, con molti suggerimenti utili (probabilmente inclusi quelli riguardanti le tue lingue preferite ;-) così come una trattazione matematica leggermente più rigorosa.

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 si potrebbe passare dalla massa della terra alla funzione e poi 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.

Il currying è una trasformazione che può essere applicata alle funzioni per consentire loro di accettare un argomento in meno rispetto a prima.

Ad esempio, in F# puoi definire una funzione in questo modo: -

let f x y z = x + y + z

Qui la funzione f prende i parametri x, yez e li somma insieme così: -

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 -> f x" è una funzione lambda equivalente a x => f(x) in C#.Questa funzione immette la funzione che desideri eseguire e restituisce una funzione che accetta un singolo argomento e restituisce la funzione specificata con il primo argomento impostato sull'argomento di 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 equivalente a f1 y z = 1 + y + z.Ciò significa che possiamo fare quanto segue: -

f1 2 3

Che restituisce 6.

Questo processo viene spesso confuso con l'"applicazione di una funzione parziale" che può essere definita così: -

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 in meno e, come mostrano i due esempi precedenti, è implementata direttamente nella definizione della funzione F# standard in modo da poter ottenere il risultato precedente in questo modo: -

let f1 = f 1
f1 2 3

Che restituirà un risultato di 6.

Insomma:-

La differenza tra l'applicazione di currying e l'applicazione di funzioni parziali è che: -

Il currying accetta una funzione e fornisce una nuova funzione che accetta un singolo argomento e restituisce la funzione specificata con il suo primo argomento impostato su quell'argomento. Ciò ci consente di rappresentare funzioni con più parametri come una serie di funzioni a singolo argomento.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 parziale della funzione è 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 il file 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 fai questo:

 addTen(4)

è lo stesso di:

function(4) { return 10 + 4} // 14

Quindi il nostro addTen() aggiunge sempre dieci a qualunque cosa passiamo.Possiamo creare 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...

Una funzione curry è una funzione di diversi argomenti riscritta in modo tale da accettare il primo argomento e restituire una funzione che accetta il secondo argomento e così via.Ciò consente alle funzioni con più argomenti di applicare parzialmente alcuni dei loro argomenti iniziali.

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..."

(Semplicemente usando la concatenazione tramite + per evitare distrazioni per i programmatori non Python.)

Modifica per aggiungere:

Vedere http://docs.python.org/library/functools.html?highlight=partial#functools.partial, che mostra anche l'oggetto parziale vs.distinzione di funzioni nel modo in cui Python la implementa.

Se capisci partial sei a metà strada.L'idea di partial consiste nel preapplicare argomenti a una funzione e restituire una nuova funzione che richiede solo gli argomenti rimanenti.Quando viene chiamata questa nuova funzione, include gli argomenti precaricati insieme a tutti gli argomenti che le sono stati forniti.

A Clojure + è una funzione ma per rendere le cose assolutamente chiare:

(defn add [a b] (+ a b))

Potresti essere consapevole che il inc la funzione aggiunge semplicemente 1 a qualunque numero venga passato.

(inc 7) # => 8

Costruiamolo noi stessi utilizzando partial:

(def inc (partial add 1))

Qui restituiamo un'altra funzione che ha 1 caricato nel primo argomento di add.COME add accetta due argomenti come nuovi inc la funzione vuole solo il file b argomento - non 2 argomenti come prima poiché 1 è già stato parzialmente applicato.Così partial è uno strumento da cui creare nuove funzioni con valori predefiniti preforniti.Ecco perché in un linguaggio funzionale le funzioni spesso ordinano gli argomenti dal generale allo specifico.Ciò rende più semplice riutilizzare tali funzioni da cui costruire altre funzioni.

Ora immagina se il linguaggio fosse abbastanza intelligente da capirlo introspettivamente add voleva due argomenti.Quando gli abbiamo passato un argomento, invece di esitare, cosa sarebbe successo se la funzione avesse applicato parzialmente l'argomento lo avessimo passato per nostro conto, capendo che probabilmente intendevamo fornire l'altro argomento in seguito?Potremmo quindi definire inc senza utilizzare esplicitamente partial.

(def inc (add 1)) #partial is implied

Questo è il modo in cui si comportano alcune lingue.È eccezionalmente utile quando si desidera comporre funzioni in trasformazioni più grandi.Ciò porterebbe ai trasduttori.

Ho trovato utile questo articolo, e l'articolo a cui fa riferimento, per comprendere meglio il currying:http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

Come menzionato dagli altri, è solo un modo per avere una funzione con un parametro.

Ciò è utile in quanto non è necessario presupporre quanti parametri verranno passati, quindi non sono necessarie funzioni a 2 parametri, 3 parametri e 4 parametri.

Curry può semplificare il tuo codice.Questo è uno dei motivi principali per utilizzarlo.Il currying è un processo di conversione di una funzione che accetta n argomenti in n funzioni che accettano un solo argomento.

Il principio è passare gli argomenti della funzione passata, utilizzando la proprietà closing (chiusura), per memorizzarli in un'altra funzione e trattarla come un valore restituito, e queste funzioni formano una catena e gli argomenti finali vengono passati per completare l'operazione.

Il vantaggio di ciò è che può semplificare l'elaborazione dei parametri trattando un parametro alla volta, il che può anche migliorare la flessibilità e la leggibilità del programma.Ciò rende anche il programma più gestibile.Anche dividere il codice in parti più piccole lo renderebbe più facile da riutilizzare.

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 è molto utile per rendere ordinato il codice complesso e gestire metodi non sincronizzati, ecc.

Il currying sta traducendo una funzione da callable as f(a, b, c) in richiamabile come f(a)(b)(c).

Altrimenti il ​​currying avviene quando si scompone una funzione che accetta più argomenti in una serie di funzioni che prendono parte degli argomenti.

Letteralmente, currying è una trasformazione di funzioni:da un modo di chiamare ad un altro.In JavaScript, di solito creiamo un wrapper per mantenere la funzione originale.

Il curry non chiama una funzione.Lo trasforma e basta.

Creiamo una funzione curry che esegue il currying per funzioni a due argomenti.In altre parole, curry(f) per due argomenti f(a, b) lo traduce in f(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.

  • Il risultato di curry(func) è un involucro function(a).
  • Quando si chiama like sum(1), l'argomento viene salvato nell'ambiente lessicale e viene restituito un nuovo wrapper function(b).
  • Poi sum(1)(2) finalmente chiama function(b) fornendo 2, e passa la chiamata alla somma multi-argomento originale.

Una funzione al curry viene applicata a più elenchi di argomenti, anziché solo a uno.

Ecco una funzione regolare e non curva, 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

Ciò che accade qui è questo quando invochi curriedSum, in realtà ottieni due invocazioni di funzioni tradizionali una dopo l'altra.La prima invocazione della funzione prende un singolo parametro int chiamato x e restituisce un valore di funzione per la seconda funzione.Questa seconda funzione accetta il parametro Inty.

Ecco una funzione chiamata first questo fa nello spirito di ciò che la prima invocazione tradizionale curriedSum farebbe:

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

Applicare 1 alla prima funzione - in altre parole, invocando la prima funzione e passando in 1 —Isula 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 currying potrebbe essere quando si hanno funzioni di cui al momento si conosce solo uno dei parametri:

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 conosci il secondo parametro per la richiamata quando lo invii a performAsyncRequest(_:) dovresti creare un'altra lambda/chiusura per inviarla alla funzione.

Come tutte le altre risposte, il curry aiuta a creare funzioni parzialmente applicate.Javascript non fornisce supporto nativo per il currying automatico.Pertanto gli esempi forniti sopra potrebbero non aiutare nella codifica pratica.C'è qualche esempio eccellente in livescript (che essenzialmente viene compilato 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 fornito meno argomenti, livescript genera una nuova funzione al curry per te (doppia)

Qui puoi trovare una semplice spiegazione dell'implementazione del curry in C#.Nei commenti, ho cercato di mostrare come il currying 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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top