题
任何人都有体面的例子,最好的实用/用,它们可以证明概念?
解决方案
(编辑:一个小小的 Ocaml FP公案 要启动的东西)
该公案的扩充(有执关于食品,那不是食物)
一个学生来到雅克灌木丛和所说的,"我不了解什么讨好是好的。" Jacques回答说,"告诉我你最喜欢的餐和你最喜欢的甜点"。不解的学生回答说他喜欢煎饼和寒天,但是同时他最喜欢的餐厅服务伟大的煎饼,他们的寒天总是给了他一个胃痛。因此雅克采取的学生在餐厅吃饭,提供煎饼的每一位作为良好的学生喜爱,然后把他整个城市给一个商店,作了出色的寒天这里的学生愉快地适用的其余部分他的胃口。该学生是满足,但他不是开明的...直到第二天早晨当他醒来的时候和他的胃感觉很好。
我的例子将涵盖使用它的再利用和封装的代码。这是相当明显的一旦你看看这些,应该给你一个具体的、简单的例子,你能想到的申请,在许多情况下。
我们想要做一张地图在一棵树。这种功能可以扩充和加到每个节点,如果它需要更多那么一个参数--因为我们可以应用的一个节点,因为它是最终的论点。它没有要咖喱,而写 另一个 功能(假定这一功能是被用来在其他情况下与其他变量)将是一种浪费。
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
所以这种简单的情况不能令人信服的。这真的是虽然和强大的,一旦你使用的语言更为自然遇到这些情况。其他例子有一些代码的再利用作为讨好.一个 复发的关于总理创建的数字.可怕的很多相似性:
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是扩充职能,因为他们有免费的变量,我们可以得到任何索引的,它的顺序不知道或担心f_recurrence.
其他提示
虽然前面的例子回答了这个问题,这里有两个简单的例子如何扩充,可以有利于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家庭的功能!在扩充的版本,注意到明显缺乏的一个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];;
扩充描述了过程中的转化与功能多个参数纳入一个链的单参数功能。例如,三个参数功能:
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));
}
现在,布尔参数可能是 不 该论点你会最有可能要离开一个局部应用程序。这是一个原因为什么顺序的论点F#职能似乎有点奇怪。让的定义不同的C#咖喱功能:
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);
}
现在,我们可以做一些更有用的:
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));
}
}
}
为什么是这些例子在C#?因为在F#、功能的声明是咖喱的默认。你通常不需要咖喱功能;他们已经咖喱.主要的例外是框架的方法和其他载的职能,这一组包含多方面的参数。因此,您可能想要咖喱这样的功能,而且,事实上,我来到这个问题时,我是在寻找一个图书馆的功能,将这样做。我想这是缺失(如果的确是这样),因为它是相当琐碎的实施:
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"
得到周围的失败串。比较,因为据我可以告诉我们无法指定其中3个论点载于选择,可以使用一个不一般的解决方案:
let curryCompare s1 s2 (b:bool) = String.Compare(s1, s2, b)
let backwardsCurryCompare (b:bool) s1 s2 = String.Compare(s1, s2, b)
我不会去详细介绍使用的部分功能的应用程序在F#因为其他的答案已经涵盖了。
这是一个相当简单的过程。采取一种功能结合它的一个参数和返回的一个新的功能。例如:
let concatStrings left right = left + right
let makeCommandPrompt= appendString "c:\> "
现在通过扩充的简单concatStrings功能,可以很容易地加DOS式命令提到前面的任何弦!真的有用!
好吧,不是真的。一个更有用的情况下,我找到的是当我想要有一个让一个函数,返回我的数据流等方式。
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.
便利的一部分,而不是创建一整类的这种事情,呼吁本构造,呼吁obj。readDWORD(),你只有一个功能,这不可能是突出在你的。
你知道你可以地图的功能超过一个清单?例如,映射一个函数添加一个以每个元素的清单:
> List.map ((+) 1) [1; 2; 3];;
val it : int list = [2; 3; 4]
这实际上已经使用讨好的,因为 (+)
操作员用来创建一个函数添加一个为其论据,但你可以挤一点更多的是出的这个例子通过改变它的地图相同功能的一个列表,列表:
> List.map (List.map ((+) 1)) [[1; 2]; [3]];;
val it : int list = [[2; 3]; [4]]
不讨好你不可以部分地适用这些功能和将要写这样的东西代替:
> List.map((fun xs -> List.map((fun n -> n + 1), xs)), [[1; 2]; [3]]);;
val it : int list = [[2; 3]; [4]]
我给了一个很好的例的模拟讨好C# 在我的博客.其要旨是,您可以创建一个函数,是关闭了一个参数(在我的例子创建一个函数,用于计算销售税封闭的价值给予市政府)的一个现有的多参数功能。
是什么吸引人这,而不是使一个单独的功能专门用于计算销售税,在库克县,您可以创建(和重复使用)的动态功能在运行时间。