Curry описывает процесс преобразования функции с несколькими аргументами в цепочку функций с одним аргументом. Пример в C # для функции с тремя аргументами:
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"
Чтобы обойти ошибку с помощью String.Compare, поскольку, насколько я могу судить, невозможно указать, какую перегрузку с тремя аргументами выбрать, вы можете использовать необщее решение:
let curryCompare s1 s2 (b:bool) = String.Compare(s1, s2, b)
let backwardsCurryCompare (b:bool) s1 s2 = String.Compare(s1, s2, b)
Я не буду вдаваться в подробности об использовании частичного применения функции в F #, потому что другие ответы уже рассмотрели это.