Question

Quelqu'un a un décent exemple, de préférence pratique/utiles, ils pourraient post démontrer le concept?

Était-ce utile?

La solution

(Edit:un petit Ocaml FP Koan pour démarrer les choses)

Le Koan de Nourrissage (Un koan au sujet de la nourriture, qui n'est pas à propos de la nourriture)

Une étudiante est venue à Jacques Garrigue et dit, "je ne comprends pas ce lancer est bon pour l'." Jacques a répondu: "Dites-moi votre plat préféré et votre dessert favori".L'perplexe élève a répondu qu'il aimait okonomiyaki et kanten, mais alors que son restaurant préféré servi de grandes okonomiyaki, leur kanten lui donnait toujours mal au ventre le matin suivant.Si Jacques a pris l'étudiant à manger dans un restaurant qui servait des okonomiyaki tout aussi bon que l'élève favori, puis l'a conduit à travers la ville dans une boutique qui fait d'excellents kanten où l'étudiant heureusement appliqué le reste de son appétit.L'étudiant était saturé, mais il n'était pas éclairée ...jusqu'à ce que le lendemain matin, quand il se réveilla, et son estomac se sentait bien.

Mes exemples de couverture de l'utiliser pour la réutilisation et l'encapsulation de code.C'est assez évident, une fois que vous regardez ces et devrait vous donner un béton, exemple simple que vous pouvez penser à appliquer dans de nombreuses situations.

Nous voulons faire une carte sur un arbre.Cette fonction pourrait être au curry et appliquée à chaque nœud s'il a besoin de plus d'un argument, puisque nous serions en application de l'un au niveau du noeud que c'est la plaidoirie finale.Il n'a pas à être au curry, mais l'écriture un autre fonction (en supposant que cette fonction est utilisée dans d'autres cas avec d'autres variables) serait un gaspillage.

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

mais c'est la même chose que:

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

Donc ce cas simple, n'est pas convaincante.C'est vraiment bien, et puissant une fois que vous utilisez la langue la plus et viennent naturellement dans l'ensemble de ces situations.L'autre exemple avec un peu de code de la réutilisation comme nourrissage.Un la relation de récurrence pour créer des nombres premiers.Beaucoup de similitude là:

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, maintenant rowland et cloitre sont des fonctions curryfiées, depuis qu'ils ont des variables libres, et on peut obtenir toutes les index de la séquence sans le savoir ou s'inquiéter de la f_recurrence.

Autres conseils

Alors que les exemples précédents répondu à la question, voici deux simples exemples de la façon dont Nourrissage peut être bénéfique pour la programmation 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..."

Et n'oubliez pas que vous pouvez curry le Printf de la famille de fonction!Dans le curry de version, notez l'absence évidente d'un 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];;

Nourrissage décrit le processus de transformation d'une fonction à plusieurs arguments en une chaîne unique argument des fonctions.Exemple en C#, pour une période de trois argument de la fonction:

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));
}

Maintenant, l'argument booléen est probablement pas l'argument que vous souhaitez probablement laisser ouvrir avec une application partielle.C'est une des raisons pourquoi l'ordre des arguments dans les fonctions F# peut sembler un peu bizarre au premier abord.Nous allons définir un autre C# curry fonction:

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);
}

Maintenant, nous pouvons faire quelque chose d'un peu plus utile:

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));
        }
    }
}

Pourquoi ces exemples en C#?Parce que en F#, les déclarations de fonction sont au curry par défaut.Vous n'avez généralement pas besoin de curry fonctions;ils sont déjà au curry.La principale exception à cette règle est le cadre de méthodes et d'autres fonctions surchargées, qui prennent un tuple contenant plusieurs arguments.Par conséquent, vous pourriez curry de telles fonctions, et, en fait, je suis tombé sur cette question quand je cherchais une fonction de la bibliothèque, pour ce faire.Je suppose qu'il est manquant (si vraiment c'est) parce que c'est assez trivial à mettre en œuvre:

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"

Pour obtenir autour de l'échec, avec de la Ficelle.Comparez, depuis aussi loin que je peux voir, il n'y a aucun moyen de spécifier 3-l'argument de la surcharge de sélection, vous pouvez utiliser une non-solution générale:

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

Je n'entrerai pas dans les détails sur les utilisations de la fonction partielle de l'application en F#, parce que les autres réponses ont couvert que déjà.

C'est un processus assez simple.Prendre une fonction, se lier à l'un de ses arguments et retourne une nouvelle fonction.Par exemple:

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

Maintenant par lancer le simple concatStrings fonction, vous pouvez facilement ajouter un DOS de style de l'invite de commande à l'avant de n'importe quelle chaîne!Vraiment utile!

Bon, pas vraiment.Plus de cas utile je trouve, c'est quand je veux faire une fonction qui me renvoie des données dans un flux de la même manière.

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.

La pratique, c'est que, plutôt que de créer une classe entière pour ce genre de chose, appeler le constructeur, l'appel de l'obj.readDWORD(), vous avez juste une fonction qui ne peut pas être muté sous vous.

Vous savez que vous pouvez mapper une fonction sur une liste?Par exemple, le mappage d'une fonction pour ajouter un à chaque élément d'une liste:

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

C'est en fait déjà à l'aide de nourrissage parce que le (+) l'opérateur a été utilisé pour créer une fonction pour ajouter un argument, mais vous pouvez presser un peu plus de cet exemple en le modifiant pour la même fonction d'une liste de listes:

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

Sans nourrissage vous ne pouviez pas appliquer partiellement ces fonctions et qui aurait pour écrire quelque chose comme ceci à la place:

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

J'ai donné un bon exemple de la simulation de nourrissage en C# sur mon blog.L'essentiel est que vous pouvez créer une fonction qui est fermé sur un paramètre (dans mon exemple créer une fonction pour le calcul de la taxe de vente clôturé au-dessus de la valeur d'une municipalité donnée)d'un existant multi-paramètre de la fonction.

Ce qui est attrayant ici, c'est plutôt de devoir faire une fonction distincte spécialement pour le calcul de la taxe de vente dans le Comté de Cook, vous pouvez créer (et réutiliser) la fonction dynamiquement à l'exécution.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top