任何人都有一个体面的例子,最好是实用/有用,他们可以发布演示概念吗?
(编辑:小型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如何有利于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描述了将具有多个参数的函数转换为单参数函数链的过程.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#中部分函数应用程序的用法,因为其他答案已经涵盖了这一点.