F # funcția curried

Oricine are un exemplu decent, de preferat practic/util, ar putea posta demonstrarea conceptului?

0
fr hi bn

6 răspunsuri

(Editați: Ocaml FP Koan pentru a începe lucrurile)

     
    

Koan of Currying (un koan despre alimente, care nu este vorba despre alimente)

         
      

Un student a venit la Jacques Garrigue și a spus: "Nu înțeleg ce este bine pentru currying." Jacques a răspuns: "Spune-mi masa ta preferată și desertul tău preferat". Studentul nedumerit a răspuns că îi plăcea okonomiyaki și kanten, dar în timp ce restaurantul său favorit a servit oconomiyaki minunat, kantenul ia dat întotdeauna o durere de stomac în dimineața următoare. Jacques a luat studentul să mănânce la un restaurant care a servit okonomiyaki la fel de bun ca favoritul elevului, apoi l-au dus în oraș într-un magazin care a făcut excelent kanten în cazul în care studentul a aplicat fericit restul apetitului său. Studentul a fost saturat, dar el nu a fost iluminat ... pana dimineata urmatoare, cand sa trezit si stomacul sa simtit bine.

    
  

Exemplele mele vor acoperi utilizarea acestuia pentru reutilizarea și încapsularea codului. Acest lucru este destul de evident odată ce te uiți la acestea și ar trebui să vă dau un exemplu concret și simplu pe care îl puteți gândi să îl aplicați în numeroase situații.

Vrem să facem o hartă pe un copac. Această funcție poate fi currată și aplicată fiecărui nod în cazul în care are nevoie de mai mult de un argument - de vreme ce aplicăm pe nod ca fiind ultimul argument. Nu trebuie să fie curried, dar scrierea unei alte funcții (presupunând că această funcție este folosită în alte cazuri cu alte variabile) ar fi o pierdere.

type 'a tree = E of 'a | N of 'a * 'a tree * 'a tree
let rec tree_map f tree = match tree with
    | N(x,left,right) -> N(f x, tree_map f left, tree_map f right)
    | E(x) -> E(f x)

let sample_tree = N(1,E(3),E(4)
let multiply x y = x * y
let sample_tree2 = tree_map (multiply 3) sample_tree

dar acesta este același lucru cu:

let sample_tree2 = tree_map (fun x -> x * 3) sample_tree

Deci, acest caz simplu nu este convingător. Este într-adevăr, totuși, și puternic, odată ce folosiți limba, mai întâlnim aceste situații. Celălalt exemplu, cu o reutilizare a codului ca și currying. Relația de recurență pentru a crea numere prime . O mulțime de asemănări acolo:

let rec f_recurrence f a seed n =
    match n with
    | a -> seed
    | _ -> let prev = f_recurrence f a seed (n-1) in
           prev + (f n prev)

let rowland = f_recurrence gcd 1 7
let cloitre = f_recurrence lcm 1 1

let rowland_prime n = (rowland (n+1)) - (rowland n)
let cloitre_prime n = ((cloitre (n+1))/(cloitre n)) - 1

Ok, acum râul și cloitrul sunt funcții curried, deoarece au variabile libere și putem obține orice indice de secvență fără să știm sau să ne îngrijorăm de f_recurrence.

0
adăugat
Acest răspuns descrie aplicarea parțială a funcției, care are legătură cu currying-ul, dar nu același lucru
adăugat autor phoog, sursa

Este un proces destul de simplu. Luați o funcție, legați unul dintre argumentele sale și reveniți la o funcție nouă. De exemplu:

let concatStrings left right = left + right
let makeCommandPrompt= appendString "c:\> "

Acum, prin curtarea funcției simple concatStrings, puteți adăuga cu ușurință o comandă de comandă a stilului DOS în fața oricărui șir! Foarte util!

Bine, nu chiar. Un caz mai util mi se pare este atunci când vreau să am o fac o funcție care mi returnează date într-un flux mod similar.

let readDWORD array i = array[i] | array[i + 1] << 8 | array[i + 2] << 16 | 
    array[i + 3] << 24 //I've actually used this function in Python.

Partea convenabilă despre aceasta este că, mai degrabă decât să creezi o clasă întreagă pentru acest tip de lucru, chemând constructorul, apelând obj.readDWORD (), ai doar o funcție care nu poate fi mutată din sub tine.

0
adăugat
Acest răspuns descrie aplicarea parțială a funcției, care are legătură cu currying-ul, dar nu același lucru
adăugat autor phoog, sursa

În timp ce exemplele anterioare au răspuns la întrebare, iată două exemple mai simple despre modul în care Currying poate fi benefic pentru programarea F #.

open System.IO

let appendFile (fileName : string) (text : string) =
    let file = new StreamWriter(fileName, true)
    file.WriteLine(text)
    file.Close()

// Call it normally    
appendFile @"D:\Log.txt" "Processing Event X..."

// If you curry the function, you don't need to keep specifying the
// log file name.
let curriedAppendFile = appendFile @"D:\Log.txt"

// Adds data to "Log.txt"
curriedAppendFile "Processing Event Y..."

Și nu uitați că puteți curma familia Printf de funcție! În versiunea curried, observați lipsa distinctă a unei lambda.

// Non curried, Prints 1 2 3 
List.iter (fun i -> printf "%d " i) [1 .. 3];;

// Curried, Prints 1 2 3
List.iter (printfn "%d ") [1 .. 3];;
0
adăugat
Acest răspuns descrie aplicarea parțială a funcției, care are legătură cu currying-ul, dar nu același lucru
adăugat autor phoog, sursa

Știi că poți să-ți mapi o funcție pe o listă? De exemplu, maparea unei funcții pentru a adăuga una la fiecare element dintr-o listă:

> List.map ((+) 1) [1; 2; 3];;
val it : int list = [2; 3; 4]

De fapt, deja se folosește currying deoarece operatorul (+) a fost folosit pentru a crea o funcție pentru a adăuga unul la argumentul său, dar puteți stoarce puțin mai mult din acest exemplu, modificând-o pentru a mapa aceeași funcție a unei liste de liste:

> List.map (List.map ((+) 1)) [[1; 2]; [3]];;
val it : int list = [[2; 3]; [4]]

Fără currying nu ai putea să aplici parțial aceste funcții și ar trebui să scrie ceva de genul asta:

> List.map((fun xs -> List.map((fun n -> n + 1), xs)), [[1; 2]; [3]]);;
val it : int list = [[2; 3]; [4]]
0
adăugat
@phoog Acest răspuns explică în mod corect faptul că "Fără currying nu ai putut aplica parțial aceste funcții".
adăugat autor Jon Harrop, sursa
Acest răspuns descrie aplicarea parțială a funcției, care are legătură cu currying-ul, dar nu același lucru
adăugat autor phoog, sursa

Currying descrie procesul de transformare a unei funcții cu multiple argumente într-un lanț de funcții cu un singur argument. Exemplu în C#, pentru o funcție cu trei argumente:

Func>> Curry(Func f)
{
    return a => b => c => f(a, b, c);
}

void UseACurriedFunction()
{
    var curryCompare = Curry(String.Compare);
    var a = "SomeString";
    var b = "SOMESTRING";
    Console.WriteLine(String.Compare(a, b, true));
    Console.WriteLine(curryCompare(a)(b)(true));

    //partial application
    var compareAWithB = curryCompare(a)(b);
    Console.WriteLine(compareAWithB(true));
    Console.WriteLine(compareAWithB(false));
}

Acum, argumentul boolean este probabil nu argumentul pe care cel mai probabil doriți să îl lăsați deschis cu o aplicație parțială. Acesta este motivul pentru care ordinea argumentelor din funcțiile F # poate părea puțin ciudat la început. Să definim o altă funcție C# curry:

Func>> BackwardsCurry(Func f)
{
    return a => b => c => f(c, b, a);
}

Acum, putem face ceva mai util:

void UseADifferentlyCurriedFunction()
{
    var curryCompare = BackwardsCurry(String.Compare);

    var caseSensitiveCompare = curryCompare(false);
    var caseInsensitiveCompare = curryCompare(true);

    var format = Curry(String.Format)("Results of comparing {0} with {1}:");

    var strings = new[] {"Hello", "HELLO", "Greetings", "GREETINGS"};

    foreach (var s in strings)
    {
        var caseSensitiveCompareWithS = caseSensitiveCompare(s);
        var caseInsensitiveCompareWithS = caseInsensitiveCompare(s);
        var formatWithS = format(s);

        foreach (var t in strings)
        {
            Console.WriteLine(formatWithS(t));
            Console.WriteLine(caseSensitiveCompareWithS(t));
            Console.WriteLine(caseInsensitiveCompareWithS(t));
        }
    }
}

De ce sunt aceste exemple în C #? Deoarece în F #, declarațiile de funcții sunt currată în mod implicit. De obicei, nu aveți nevoie să curriți funcțiile; sunt deja curry. Excepția majoră la aceasta sunt metodele-cadru și alte funcții supraîncărcate, care iau o tuplă care conținea mai multe argumente. Prin urmare, ați putea dori să curriți astfel de funcții și, de fapt, am ajuns la această întrebare când căutam o funcție de bibliotecă care să facă acest lucru. Presupun că lipsește (dacă într-adevăr este) pentru că este destul de banal să pună în aplicare:

let curry f a b c = f(a, b, c)

//overload resolution failure: there are two overloads with three arguments.
//let curryCompare = curry String.Compare

//This one might be more useful; it works because there's only one 3-argument overload
let backCurry f a b c = f(c, b, a)
let intParse = backCurry Int32.Parse
let intParseCurrentCultureAnyStyle = intParse CultureInfo.CurrentCulture NumberStyles.Any
let myInt = intParseCurrentCultureAnyStyle "23"
let myOtherInt = intParseCurrentCultureAnyStyle "42"

Pentru a rezolva eșecul cu String.Compare, din moment ce pot spune că nu există nici o modalitate de a specifica care suprasarcină cu 3 argumente pentru a alege, puteți utiliza o soluție non-generală:

let curryCompare s1 s2 (b:bool) = String.Compare(s1, s2, b)
let backwardsCurryCompare (b:bool) s1 s2 = String.Compare(s1, s2, b)

Nu voi intra în detalii cu privire la utilizarea funcției parțiale de funcții în F # deoarece celelalte răspunsuri au acoperit deja acest lucru.

0
adăugat
Acesta ar trebui să fie răspunsul acceptat
adăugat autor Sammy S., sursa

Am dat un bun exemplu de simulare a currying-ului în C# pe blogul meu . Principalul lucru este că puteți crea o funcție închisă peste un parametru (în exemplul meu, creați o funcție pentru calcularea impozitului pe vânzări închis peste valoarea unei municipalități date) dintr-o funcție multiparametru existentă.

Ceea ce este atrăgător aici este în loc de a trebui să faci o funcție separată special pentru calculul impozitului pe vânzări în Cook County, puteți crea (și reutiliza) funcția dinamic la timpul de execuție.

0
adăugat
+1, deși link-ul dvs. de blog pare să fie rupt, bănuiesc că exemplul dvs. a arătat funcția reală currying în C#, nu simulată. Acest raspuns este singurul care descrie de fapt currying-ul ca lucru care permite aplicarea partiala a functiilor, mai degraba decat confundarea cu aplicatii functionale partiale.
adăugat autor phoog, sursa
Recent am migrat blogul meu de la CommunityServer la Sitefinity. Nu am ajuns în jurul pentru a scrie un utilitar pentru a importa datele vechi blog-ul meu: (dar am folosit funcția supraîncărcare pentru a simula currying.Dacă treceți ambii parametri, veți obține rezultatul, dacă treci unul, veți obține un func care ia și întoarce rezultatul. Nu la fel de elegant ca adevăratul currying dar funcționează;)
adăugat autor Michael Brown, sursa