Frage

Wer noch ein anständiges Beispiel, vorzugsweise praktisch/nützlich, Sie posten könnte, demonstriert das Konzept?

War es hilfreich?

Lösung

(Bearbeiten:eine kleine Ocaml-FP Koan um Dinge zu beginnen)

Das Koan von Currying (A koan, über Essen, das nicht über die Nahrung)

Ein Schüler kam zu Jacques Garrigue und sagte, "ich verstehe nicht, was currying ist gut." Jacques antwortete, "Sagen Sie mir Ihre Lieblings-Essen und Ihre Lieblings-dessert".Der verblüffte student antwortete, dass er gerne okonomiyaki und kanten, aber während seiner Lieblings-restaurant serviert große okonomiyaki, deren kanten immer gab ihm ein Magenschmerzen am nächsten morgen.So Jacques nahm der student zu Essen in einem restaurant, serviert okonomiyaki jedes bit so gut wie der student ist Favorit, dann nahm ihn quer durch die Stadt, um ein Geschäft, das in hervorragender Weise die kanten, wo die Schüler gerne angewendet, den Rest seines Appetits.Der student wurde satt, aber er war nicht erleuchtet ...bis zum nächsten morgen, als er aufwachte und sein Magen fühlte sich gut.

Meine Beispiele Abdeckung mit es für die Wiederverwendung und Kapselung von code.Dies ist ziemlich offensichtlich, wenn Sie sich diese und geben Ihnen ein konkretes, einfaches Beispiel, das man sich denken kann Anwendung in zahlreichen Situationen.

Wir wollen, zu tun, eine Karte über einen Baum.Diese Funktion könnte sein, Curry-und auf jeden Knoten angewendet, falls es mehr als ein argument-da wären wir bewerben Sie auf den Knoten, wie es die Letzte argument.Es muss nicht Curry, sondern schreiben andere Funktion (sofern diese Funktion verwendet wird, in anderen Fällen mit anderen Variablen) wäre eine Verschwendung.

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

aber das ist das gleiche wie:

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

So in diesem einfachen Fall nicht zu überzeugen.Es ist wirklich obwohl, und leistungsstarke wenn Sie die Sprache besser und natürlich kommen in diesen Situationen.Das andere Beispiel mit einigen Wiederverwendung von code als currying.Ein recurrence relation zu erstellen Primzahlen.Sehr viel ähnlichkeit da:

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, jetzt rowland und cloitre sind Curry-Funktionen, da Sie über freien Variablen, und wir können jeden index der Folge, ohne zu wissen oder sich Gedanken über f_recurrence.

Andere Tipps

Während die vorherigen Beispiele, die die Frage beantwortet, hier sind zwei einfachere Beispiele, wie Currying kann vorteilhaft sein, für F# - Programmierung.

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

Und vergessen Sie nicht, Sie können curry die Familie der Printf-Funktion!In die Curry-version, beachten Sie die deutlichen Mangel einer 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];;

Currying beschreibt den Prozess der Umwandlung einer Funktion mit mehreren Argumenten in eine Kette von single-argument-Funktionen.Beispiel in C#, um eine drei-argument-Funktion:

Func<T1, Func<T2, Func<T3, T4>>> Curry<T1, T2, T3, T4>(Func<T1, T2, T3, T4> f)
{
    return a => b => c => f(a, b, c);
}

void UseACurriedFunction()
{
    var curryCompare = Curry<string, string, bool, int>(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));
}

Nun, das Boolesche argument ist wohl nicht das argument, würden Sie wahrscheinlich wollen, zu verlassen, öffnen Sie mit einer teilbremsung.Dies ist ein Grund, warum die Reihenfolge der Argumente in F# - Funktionen kann scheinen ein wenig seltsam auf den ersten.Wir definieren eine andere C# - curry-Funktion:

Func<T3, Func<T2, Func<T1, T4>>> BackwardsCurry<T1, T2, T3, T4>(Func<T1, T2, T3, T4> f)
{
    return a => b => c => f(c, b, a);
}

Jetzt können wir etwas tun, ein wenig mehr nützlich:

void UseADifferentlyCurriedFunction()
{
    var curryCompare = BackwardsCurry<string, string, bool, int>(String.Compare);

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

    var format = Curry<string, string, string, string>(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));
        }
    }
}

Warum sind diese Beispiele in C#?Da in F# -, Funktions-Deklarationen Curry standardmäßig.Sie müssen sich für gewöhnlich nicht an curry-Funktionen;Sie sind bereits mit Curry.Die große Ausnahme ist die framework-Methoden und andere überladene Funktionen, die ein Tupel enthält mehrere Argumente.Sie daher vielleicht möchten curry solche Funktionen, und, in der Tat, ich kam auf diese Frage, wenn ich war auf der Suche nach einer library-Funktion, dies zu tun wäre.Ich nehme an, es fehlt (wenn ja, ist es), weil es ziemlich trivial zu implementieren:

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"

Zu bekommen, um den Fehler mit String.Vergleichen, da so weit wie ich kann sagen, es gibt keine Möglichkeit zu geben, die 3-argument überlastung zu Holen, die Sie verwenden können, ein nicht-Allgemeine Lösung:

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

Ich werde nicht ins detail gehen über die Anwendungen der partiellen Funktion Anwendung in F#, weil die anderen Antworten abgedeckt haben, das schon.

Es ist ein ziemlich einfacher Prozess.Nehmen Sie eine Funktion binden eines Ihrer Argumente und return eine neue Funktion.Zum Beispiel:

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

Jetzt durch currying die einfache concatStrings Funktion, Sie können leicht hinzufügen ein DOS-Stil-Eingabeaufforderung, um die vor-string!Wirklich nützlich!

Okay, nicht wirklich.Ein nützlicher Fall finde ich ist, wenn ich wollen haben eine Funktion, dass gibt mir die Daten in einen stream wie Art und Weise.

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.

Der praktische Teil über es ist, dass, anstatt eine ganze Klasse für diese Art der Sache, ruft den Konstruktor, Aufruf von obj.readDWORD(), Sie müssen nur eine Funktion, die können nicht mutiert unter Euch.

Sie wissen, Sie können anzeigen einer Funktion über eine Liste?Z. B. die Zuordnung einer Funktion zum hinzufügen, um jedes element einer Liste:

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

Dies ist eigentlich schon mit currying, weil die (+) Betreiber verwendet wurde, erstellen Sie eine Funktion hinzufügen, um Ihr argument, aber Sie können drücken Sie ein wenig mehr aus diesem Beispiel durch ändern es zu Karte die gleiche Funktion, die von einer Liste von Listen:

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

Ohne currying Sie konnte nicht teilweise gelten diese Funktionen und würde etwas schreiben wie diese statt:

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

Ich gab ein gutes Beispiel für die Simulation von currying in C# auf meinem blog.Das wesentliche ist, dass Sie können eine Funktion erstellen, die geschlossen über einen parameter (in meinem Beispiel erstellen Sie eine Funktion zur Berechnung der Umsatzsteuer schlossen sich über den Wert einer bestimmten Gemeinde)aus einer bestehenden multi-parameter-Funktion.

Was ist Ansprechend, hier ist anstatt eine separate Funktion, die speziell für die Berechnung der Umsatzsteuer in Cook County, die Sie erstellen können (und Wiederverwendung) die Funktion, die dynamisch zur Laufzeit.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top