Comments 37
спасибо, статья интересная.
но вот «частичная аппликация» — не лучше ли все же «частичное применение»?
так как «аппликация» четко ассоциируется с другим. а вот функцию как-раз таки «применяют».
или я не прав?
но вот «частичная аппликация» — не лучше ли все же «частичное применение»?
так как «аппликация» четко ассоциируется с другим. а вот функцию как-раз таки «применяют».
или я не прав?
0
Я не против, но судя по всему, это устоявшийся термин: ru.wikipedia.org/wiki/%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%B8%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5
+1
Примеры в статье лично меня скорее запутали, чем что-то пояснили, но за ссылку на термин в Википедии спасибо. Всю жизнь говорил прозой, и не знал :-) Давно использую похожие трюки по мере надобности.
Успехов.
Успехов.
+2
Если можно, приведите примеры, как вы это используете. В статье действительно они «нежизненные».
+1
Вот, например, helper для Kohana, который я написал для своего проекта.
Обе функции имеют переменное число параметров.
Вот не уверен, какое из пониманий верно:
1. Метод links_Core::get($key, ...) является каррированием для links_Core::get_csrf($csrf_token, $key, ...).
2. Метод links_Core::get($key, ...) являлся бы каррирование для links_Core::get_csrf($csrf_token, $link), где $link — результат функции links_Core::get($key, ...).
Хотя в общем-то не важно, какой из вариантов формально верен. Важно, что реализации идеи встречаются на практике.
Обе функции имеют переменное число параметров.
Вот не уверен, какое из пониманий верно:
1. Метод links_Core::get($key, ...) является каррированием для links_Core::get_csrf($csrf_token, $key, ...).
2. Метод links_Core::get($key, ...) являлся бы каррирование для links_Core::get_csrf($csrf_token, $link), где $link — результат функции links_Core::get($key, ...).
Хотя в общем-то не важно, какой из вариантов формально верен. Важно, что реализации идеи встречаются на практике.
class links_Core
{
private static $links = array();
public function get($key)
{
if(empty(self::$links))
self::$links = Kohana::config('url/my_project.links', false, true);
if(!isset(self::$links[$key]))
return false;
$args = func_get_args();
array_shift($args);
array_unshift($args, self::$links[$key]);
return url::base().call_user_func_array('sprintf', $args);
}
public function get_csrf($csrf_token, $key)
{
$args = func_get_args();
array_shift($args);
$link = call_user_func_array(array('self', 'get'), $args);
if(strpos($link, '?') !== false)
return $link.'&csrf='.$csrf_token;
else
return $link.'?csrf='.$csrf_token;
}
}
* This source code was highlighted with Source Code Highlighter.
+1
Конкретно мне такие статьи позволяют лучше ориентироваться в функциональном стиле кодирования. Кроме того, если бы речь шла не о сложении, а о более сложном алгоритме, то каррированием, как я понимаю, можно упростить реализацию, тестирование и использование.
Из реальных примеров навскидку так и нечего предложить, хотя на прошлом проекте я это использовал, но кода, который можно было бы показать, под рукой нет.
С другой стороны, вот все кто с LINQ работал должны знать такой метод:
msdn.microsoft.com/ru-ru/library/bb548658.aspx
Жесть, правда? А надо както с этим работать. Вот, умение преобразовывать функции тут как раз помогает. В том числе и понимание карринга.
Из реальных примеров навскидку так и нечего предложить, хотя на прошлом проекте я это использовал, но кода, который можно было бы показать, под рукой нет.
С другой стороны, вот все кто с LINQ работал должны знать такой метод:
msdn.microsoft.com/ru-ru/library/bb548658.aspx
Жесть, правда? А надо както с этим работать. Вот, умение преобразовывать функции тут как раз помогает. В том числе и понимание карринга.
0
можно посмотреть на F#
например, там можно делать композицию функций:
readFile «c:\test.txt» |> addToEachLine «1» |> writeFile «c:\test2.txt»
операция оператор |> берет две функции и применяет результат первой ко второй
благодаря каррированию writeFile превращается из функции с двумя аргументами в функцию с одним аргументом и ее можно подвать на вход |>
В F# большинство встроенных функций для работы со списками, например, сначала бедур дополнительные аргументы а потом сам список. В результате из них легко каррированием получаются функции обработки с одним аргуметом «список»
Или последовательность.
Например seq.map берет функцию и список и последовательность, применяет функию ко всем аргументам последовательности и возвращает последовательность результатов.
тогда Seq.map (fun x -> x + 1) вернет функцию, которя к заданной последовательности прибавит единицу.
И такие функции удобно выстраивать в цепочкит типа
[1; 2; 3] |> List.filter (fun x -> x % 2 = 0) |> List.map (fun x-> x + 1)
например, там можно делать композицию функций:
readFile «c:\test.txt» |> addToEachLine «1» |> writeFile «c:\test2.txt»
операция оператор |> берет две функции и применяет результат первой ко второй
благодаря каррированию writeFile превращается из функции с двумя аргументами в функцию с одним аргументом и ее можно подвать на вход |>
В F# большинство встроенных функций для работы со списками, например, сначала бедур дополнительные аргументы а потом сам список. В результате из них легко каррированием получаются функции обработки с одним аргуметом «список»
Или последовательность.
Например seq.map берет функцию и список и последовательность, применяет функию ко всем аргументам последовательности и возвращает последовательность результатов.
тогда Seq.map (fun x -> x + 1) вернет функцию, которя к заданной последовательности прибавит единицу.
И такие функции удобно выстраивать в цепочкит типа
[1; 2; 3] |> List.filter (fun x -> x % 2 = 0) |> List.map (fun x-> x + 1)
+1
Композиция это разве не оператор >>?
0
наверное эжто все таки application, www.c-sharpcorner.com/UploadFile/rmcochran/fsharptypes03212008225543PM/fsharptypes.aspx — вот тут написано, что это Forward Pipe operator
Кстати, благодяря тому же каррированию, вместо (fun x -> x + 1) можно писать ((+) 1)
Кстати, благодяря тому же каррированию, вместо (fun x -> x + 1) можно писать ((+) 1)
+1
UFO just landed and posted this here
это как? чем щас не нравится?
0
UFO just landed and posted this here
В stl есть хелперы bind1st и bind2nd, которые выполняют частичное применение функции 2х аргументов, позволяя вернуть функтор-замыкание уже с одним аргументом. Используется достаточно часто
+1
Я правильно понял, что основная область применения карри-функций — это функции с переменным числом параметров?
0
Я понял также. Не знаю правильно или нет. )
0
Не совсем «с переменным».
Допустим есть некая универсальная функция с кучей параметров, которая делает сложное действие А (в примерах — сложение двух чисел).
int Sum(int a, int b){ return a+b; }
А вам нужна вот в данный конкретный момент функция-частный случай первой (в примере — «увеличение на 1» — это частный случай сложения).
Нужна она вам, что передать её в качестве параметра в другую функцию:
void SomeOtherFunction(Func<int, int>);
Эта функция требует, чтобы ей передали ф-ю, принимающую один параметр int.
Вы можете объявить её вручную:
int Inc (int a) { return Sum(a, 1); }
а потом передавать Inc:
SomeOtherFunction(var1, var2, Inc);
Но вместо этого вы лучше просто скаррируете Sum: «Sum.Partial(1)», тем самым избавляясь от ненужного описания функции Inc, и получите вот такой вызов:
SomeOtherFunction(var1, var2, Sum.Partial(1));
Допустим есть некая универсальная функция с кучей параметров, которая делает сложное действие А (в примерах — сложение двух чисел).
int Sum(int a, int b){ return a+b; }
А вам нужна вот в данный конкретный момент функция-частный случай первой (в примере — «увеличение на 1» — это частный случай сложения).
Нужна она вам, что передать её в качестве параметра в другую функцию:
void SomeOtherFunction(Func<int, int>);
Эта функция требует, чтобы ей передали ф-ю, принимающую один параметр int.
Вы можете объявить её вручную:
int Inc (int a) { return Sum(a, 1); }
а потом передавать Inc:
SomeOtherFunction(var1, var2, Inc);
Но вместо этого вы лучше просто скаррируете Sum: «Sum.Partial(1)», тем самым избавляясь от ненужного описания функции Inc, и получите вот такой вызов:
SomeOtherFunction(var1, var2, Sum.Partial(1));
+1
Спасибо за статью. Я так понимаю это ваша реализация, а то я замучился искать упоминание в MSDN? :)
0
да, каррирование в C# — это уже как-то немного неожиданно, хотя в принципе после введения лямбд ничего странного вроде и нет.
хм, а написать Y-комбинатор-то получается тоже возможно, или я путаю…
так что дело за монадами ;))
хм, а написать Y-комбинатор-то получается тоже возможно, или я путаю…
так что дело за монадами ;))
+1
лямбды тут не причем. это все можно было писать еще и в C# 2.0 с анонимными методами
монады аналогично. как только появились генерики можно было писать. только вот использовать не совсем удобно было потому как не было сахара
p.s. наслаждайтесь:
delegate Func<T,R> RecFunc<T,R>(RecFunc<T,R> f);
Func<T, R> Y<T, R>(Func<Func<T, R>, Func<T, R>> f)
{
RecFunc<T, R> recFunc = r => t => f(r®)(t);
return recFunc(recFunc);
}
монады аналогично. как только появились генерики можно было писать. только вот использовать не совсем удобно было потому как не было сахара
p.s. наслаждайтесь:
delegate Func<T,R> RecFunc<T,R>(RecFunc<T,R> f);
Func<T, R> Y<T, R>(Func<Func<T, R>, Func<T, R>> f)
{
RecFunc<T, R> recFunc = r => t => f(r®)(t);
return recFunc(recFunc);
}
+1
Интересно, почему на русском это назвали каррирование… ведь на англ. яз. вроде это произносится как «кёрриинг». :)
0
Сахар это конечно хорошо, но нужно не забывать, что при применении выражений типа a => b => f(a,b), передача значения 'a' в место вызова f(a, b) происходит через поле автоматически сгенерированного класса. Т.е. выполнение этого выражения влечет за собой создание временного объекта в куче. И хотя затраты на выделение памяти в CLR сведены к минимуму, количество объектов все же может отрицательно повлиять на производительность. Другими словами, злоупотреблять сахаром не стоит — можно нажить диабет :)
+2
Вот этому как-то мало уделяют внимания, когда говорят о новых фишечках языка. Может память действительно более не ограниченный ресурс, но все таки…
0
Sign up to leave a comment.
Каррирование и частичное применение функции