Currying descrie procesul de transformare a unei funcții cu multiple argumente într-un lanț de funcții cu un singur argument. Exemplu în C#, pentru o funcție cu trei argumente:
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));
}
Acum, argumentul boolean este probabil nu argumentul pe care cel mai probabil doriți să îl lăsați deschis cu o aplicație parțială. Acesta este motivul pentru care ordinea argumentelor din funcțiile F # poate părea puțin ciudat la început. Să definim o altă funcție C# curry:
Func>> BackwardsCurry(Func f)
{
return a => b => c => f(c, b, a);
}
Acum, putem face ceva mai util:
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));
}
}
}
De ce sunt aceste exemple în C #? Deoarece în F #, declarațiile de funcții sunt currată în mod implicit. De obicei, nu aveți nevoie să curriți funcțiile; sunt deja curry. Excepția majoră la aceasta sunt metodele-cadru și alte funcții supraîncărcate, care iau o tuplă care conținea mai multe argumente. Prin urmare, ați putea dori să curriți astfel de funcții și, de fapt, am ajuns la această întrebare când căutam o funcție de bibliotecă care să facă acest lucru. Presupun că lipsește (dacă într-adevăr este) pentru că este destul de banal să pună în aplicare:
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"
Pentru a rezolva eșecul cu String.Compare, din moment ce pot spune că nu există nici o modalitate de a specifica care suprasarcină cu 3 argumente pentru a alege, puteți utiliza o soluție non-generală:
let curryCompare s1 s2 (b:bool) = String.Compare(s1, s2, b)
let backwardsCurryCompare (b:bool) s1 s2 = String.Compare(s1, s2, b)
Nu voi intra în detalii cu privire la utilizarea funcției parțiale de funcții în F # deoarece celelalte răspunsuri au acoperit deja acest lucru.