当前位置:  开发笔记 > 编程语言 > 正文

F#curried功能

如何解决《F#curried功能》经验,为你挑选了3个好方法。

任何人都有一个体面的例子,最好是实用/有用,他们可以发布演示概念吗?



1> nlucaroni..:

(编辑:小型Ocaml FP Koan开始关闭)

Currying的Koan(关于食物的公案,不是关于食物的)

一名学生来到雅克·加里格说,"我不明白什么是好事." 雅克回答说:"告诉我你最喜欢的一餐和你最喜欢的甜点".困惑的学生回答他喜欢御好烧和kanten,但他最喜欢的餐厅服务很棒的御好烧,他们的kanten第二天早上总是让他肚子疼.因此,雅克带着学生在一家餐厅吃饭,这家餐厅的服务质量和学生们最喜欢的一样好,然后带他穿过城镇去了一家商店,这家商店非常适合学生愉快地应用剩下的食欲.这名学生很满意,但他并没有开悟......直到第二天早上他醒来时,他的胃感觉很好.

我的示例将涵盖使用它来重用和封装代码.一旦你看到这些,这是相当明显的,并且应该给你一个具体的,简单的例子,你可以想到在很多情况下应用.

我们想在树上做地图.如果它需要多于一个参数,那么这个函数可以被curry并应用于每个节点 - 因为我们将在节点处应用该函数作为它的最终参数.它不必是curry,但是编写另一个函数(假设此函数在其他情况下与其他变量一起使用)将是一种浪费.

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

但这与以下相同:

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

所以这个简单的案例并不令人信服.一旦你更多地使用这种语言并且自然地遇到这些情况,它确实是非常强大的.另一个例子是将一些代码重用为currying.一个递推关系创建素数.那里有很多相似之处:

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

好吧,现在rowland和cloitre是curried函数,因为它们有自由变量,我们可以得到它的序列的任何索引而不知道或担心f_recurrence.


这个答案描述了部分功能应用,[与currying有关,但不是相同的事情](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application)

2> Chris Smith..:

虽然前面的例子回答了这个问题,但这里有两个更简单的例子,说明Currying如何有利于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..."

并且不要忘记你可以为Printf系列功能提供便利!在curried版本中,请注意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有关,但不是相同的事情](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application)

3> phoog..:

Currying描述了将具有多个参数的函数转换为单参数函数链的过程.C#中的示例,用于三参数函数:

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

现在,boolean参数可能不是您最有可能想要使用部分应用程序保持打开的参数.这就是为什么F#函数中的参数顺序最初看起来有些奇怪的一个原因.让我们定义一个不同的C#curry函数:

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

现在,我们可以做一些更有用的事情:

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

为什么这些例子在C#中?因为在F#中,函数声明默认是curry.你通常不需要咖喱功能; 他们已经咖喱了.这个的主要例外是框架方法和其他重载函数,它们使用包含多个参数的元组.因此,您可能想要讨论这些函数,事实上,当我在寻找可以执行此操作的库函数时,我遇到了这个问题.我想它是缺失的(如果确实是这样),因为它实现起来非常简单:

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"

使用String.Compare解决故障,因为据我所知,无法指定选择哪个3参数重载,您可以使用非通用解决方案:

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

我不会详细介绍F#中部分函数应用程序的用法,因为其他答案已经涵盖了这一点.

推荐阅读
mobiledu2402851377
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有