Вопрос

Я видел ссылки на функции curried в нескольких статьях и блогах, но я не могу найти хорошего объяснения (или, по крайней мере, того, которое имеет смысл!)

Это было полезно?

Решение

Каррирование - это когда вы разбиваете функцию, которая принимает несколько аргументов, на ряд функций, которые принимают часть аргументов. Вот пример в JavaScript:

function add (a, b) {
  return a + b;
}

add(3, 4); // returns 7

Это функция, которая принимает два аргумента a и b и возвращает их сумму. Теперь мы будем карри эту функцию:

function add (a) {
  return function (b) {
    return a + b;
  }
}

Это функция, которая принимает один аргумент a и возвращает функцию, которая принимает другой аргумент b, и эта функция возвращает их сумму.

add(3)(4);

var add3 = add(3);

add3(4);

Первый оператор возвращает 7, как оператор add (3, 4). Второе утверждение определяет новую функцию с именем add3, которая добавит 3 к своему аргументу. Это то, что некоторые люди могут назвать закрытием. Третье утверждение использует операцию add3 для добавления 3 к 4, в результате чего снова получается 7.

Другие советы

В алгебре функций иметь дело с функциями, которые принимают несколько аргументов (или эквивалентный один аргумент, который является N-кортежем), несколько неэлегантно - но, как доказал Моисей Шенфинкель (и, независимо, Хаскелл-карри) , это не нужно: все, что вам нужно, это функции, которые принимают один аргумент.

Итак, как вы справляетесь с тем, что вы, естественно, выражаете, например, как f (x, y) ? Ну, вы воспринимаете это как эквивалент f (x) (y) - f (x) , называете его g , это функция и вы применяете эту функцию к y . Другими словами, у вас есть только функции, которые принимают один аргумент - но некоторые из этих функций возвращают другие функции (которые также принимают один аргумент; -).

Как обычно, wikipedia имеет хорошую сводную запись об этом со многими полезными указателями (возможно, в том числе касающимися ваших любимых языки ;-), а также немного более строгая математическая обработка.

Вот конкретный пример:

Предположим, у вас есть функция, которая вычисляет гравитационную силу, действующую на объект. Если вы не знаете формулу, вы можете найти ее здесь . Эта функция принимает в качестве аргументов три необходимых параметра.

Теперь, находясь на земле, вы хотите рассчитывать силы только для объектов на этой планете. На функциональном языке вы можете передать массу земли функции и затем частично оценить ее. То, что вы получите назад, - это еще одна функция, которая принимает только два аргумента и вычисляет гравитационную силу объектов на земле. Это называется карри.

Карринг - это преобразование, которое можно применять к функциям, чтобы они могли получить на один аргумент меньше, чем ранее.

Например, в F # вы можете определить функцию следующим образом: -

let f x y z = x + y + z

Здесь функция f принимает параметры x, y и z и суммирует их вместе так: -

f 1 2 3

Возвращает 6.

Исходя из нашего определения, мы можем определить функцию карри для f: -

let curry f = fun x -> f x

Где «веселье х - > f x '- лямбда-функция, эквивалентная x = > f (x) в C #. Эта функция вводит функцию, которую вы хотите каррировать, и возвращает функцию, которая принимает один аргумент и возвращает указанную функцию с первым аргументом, установленным для входного аргумента.

Используя наш предыдущий пример, мы можем получить карри f таким образом: -

let curryf = curry f

Затем мы можем сделать следующее: -

let f1 = curryf 1

, которая предоставляет нам функцию f1, которая эквивалентна f1 y z = 1 + y + z. Это означает, что мы можем сделать следующее: -

f1 2 3

Что возвращает 6.

Этот процесс часто путают с «частичным применением функции», которое можно определить следующим образом: -

let papply f x = f x

Хотя мы можем расширить его до нескольких параметров, т. е.: -

let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.

Частичное приложение возьмет функцию и параметр (ы) и вернет функцию, которая требует один или несколько меньших параметров, и, как показывают два предыдущих примера, реализована непосредственно в стандартном определении функции F #, чтобы мы могли достичь предыдущего результата. Таким образом: -

let f1 = f 1
f1 2 3

Который вернет результат 6.

В заключение: -

Разница между карри и частичным применением функций заключается в том, что: -

Карринг принимает функцию и предоставляет новую функцию, принимающую один аргумент и возвращающую указанную функцию с первым аргументом, установленным для этого аргумента. Это позволяет нам представлять функции с несколькими параметрами в виде серии функций с одним аргументом . Пример: -

let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6

Приложение с частичной функцией более прямое: оно принимает функцию и один или несколько аргументов и возвращает функцию с первыми n аргументами, для которых задано n аргументов. Пример: -

let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6

Это может быть способ использовать функции для создания других функций.

В JavaScript:

let add = function(x){
  return function(y){ 
   return x + y
  };
};

Позвольте нам назвать это так:

let addTen = add(10);

При этом 10 передается как x ;

let add = function(10){
  return function(y){
    return 10 + y 
  };
};

что означает, что нам возвращается эта функция:

function(y) { return 10 + y };

Поэтому, когда вы звоните

 addTen();

вы действительно звоните:

 function(y) { return 10 + y };

Итак, если вы сделаете это:

 addTen(4)

это то же самое, что и

function(4) { return 10 + 4} // 14

Таким образом, наш addTen () всегда добавляет десять к тому, что мы передаем. Мы можем создавать аналогичные функции одинаковым образом:

let addTwo = add(2)       // addTwo(); will add two to whatever you pass in
let addSeventy = add(70)  // ... and so on...

Функция карри - это функция нескольких аргументов, переписанная так, что она принимает первый аргумент и возвращает функцию, которая принимает второй аргумент и так далее. Это позволяет функциям нескольких аргументов частично применять некоторые из своих начальных аргументов.

Вот игрушечный пример на Python:

>>> from functools import partial as curry

>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
        print who, 'said regarding', subject + ':'
        print '"' + quote + '"'


>>> display_quote("hoohoo", "functional languages",
           "I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."

>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")

>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."

(Просто используйте конкатенацию через +, чтобы не отвлекать программистов, не являющихся Python.)

Редактирование для добавления:

См. http://docs.python.org/ библиотека / functools.html? выделить = парциальное # functools.partial , который также показывает частичное различие между объектом и функцией в том, как Python реализует это.

Если вы понимаете частичный , вы на полпути. Идея частичного состоит в том, чтобы предварительно применить аргументы к функции и вернуть новую функцию, которая хочет только оставшиеся аргументы. Когда вызывается эта новая функция, она включает в себя предварительно загруженные аргументы вместе с любыми аргументами, которые были ей переданы.

В Clojure + - это функция, но для ясности:

(defn add [a b] (+ a b))

Возможно, вы знаете, что функция inc просто добавляет 1 к любому переданному номеру.

(inc 7) # => 8

Давайте создадим его сами, используя частичный :

(def inc (partial add 1))

Здесь мы возвращаем другую функцию, в которой 1 загружен в первый аргумент add . Поскольку add принимает два аргумента, новой функции inc требуется только аргумент b , а не 2 аргумента, как раньше, поскольку 1 уже был частично применяется. Таким образом, частичный - это инструмент, из которого можно создавать новые функции с предварительно заданными значениями по умолчанию. Вот почему в функциональном языке функции часто упорядочивают аргументы от общего к конкретному. Это облегчает повторное использование таких функций для создания других функций.

А теперь представьте, что язык достаточно умен, чтобы понять, что add требуется два аргумента. Когда мы передали ему один аргумент вместо того, чтобы прекратить, что если функция частично применила аргумент, мы передали его от нашего имени, понимая, что мы, вероятно, намеревались предоставить другой аргумент позже? Затем мы можем определить inc без явного использования частичного .

(def inc (add 1)) #partial is implied

Так ведут себя некоторые языки. Это исключительно полезно, когда кто-то хочет объединить функции в большие преобразования. Это привело бы к преобразователям.

Я нашел эту статью и статью, на которую она ссылается, полезной, чтобы лучше понять карри: http: // blogs. msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

Как уже упоминалось, это просто способ иметь функцию с одним параметром.

Это полезно, поскольку вам не нужно предполагать, сколько параметров будет передано, поэтому вам не нужны функции с двумя, тремя и четырьмя параметрами.

Карри может упростить ваш код. Это одна из главных причин использовать это. Карринг - это процесс преобразования функции, которая принимает n аргументов, в n функций, которые принимают только один аргумент.

Принцип состоит в том, чтобы передать аргументы переданной функции, используя свойство closure (closure), чтобы сохранить их в другой функции и обработать ее как возвращаемое значение, и эти функции образуют цепочку, и передаются окончательные аргументы чтобы завершить операцию.

Преимущество этого заключается в том, что он может упростить обработку параметров, работая с одним параметром за раз, что также может улучшить гибкость и удобочитаемость программы. Это также делает программу более управляемой. Кроме того, разделение кода на более мелкие части сделает его более удобным для повторного использования.

Например:

function curryMinus(x) 
{
  return function(y) 
  {
    return x - y;
  }
}

var minus5 = curryMinus(1);
minus5(3);
minus5(5);

Я тоже могу сделать ...

var minus7 = curryMinus(7);
minus7(3);
minus7(5);

Это очень удобно для сложного кода и обработки несинхронизированных методов и т. д.

Каррирование - это перевод функции из вызываемого вида в f(a, b, c) в вызываемый как f(a)(b)(c).

В противном случае каррирование - это когда вы разбиваете функцию, которая принимает несколько аргументов, на ряд функций, которые принимают часть аргументов.

Буквально, каррирование - это преобразование функций:из одного способа вызова в другой.В JavaScript мы обычно создаем оболочку для сохранения исходной функции.

Каррирование не вызывает функцию.Это просто преображает его.

Давайте создадим функцию curry, которая выполняет каррирование для функций с двумя аргументами.Другими словами, curry(f) для двух аргументов f(a, b) переводит это в f(a)(b)

function curry(f) { // curry(f) does the currying transform
  return function(a) {
    return function(b) {
      return f(a, b);
    };
  };
}

// usage
function sum(a, b) {
  return a + b;
}

let carriedSum = curry(sum);

alert( carriedSum(1)(2) ); // 3

Как вы можете видеть, реализация представляет собой серию оберток.

  • В результате curry(func) это обертка function(a).
  • Когда это называется как sum(1), аргумент сохраняется в Лексической среде, и возвращается новая оболочка function(b).
  • Тогда sum(1)(2) наконец-то звонит function(b) предоставляя 2, и он передает вызов исходной сумме с несколькими аргументами.

Функция карри применяется к нескольким спискам аргументов, а не просто один.

Вот обычная функция без карри, которая добавляет два Int параметры, x и y:

scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3

Вот аналогичная функция, которая карри. Вместо из одного списка из двух параметров Int, вы применяете эту функцию к двум спискам одного Параметр Int каждый:

scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3

То, что здесь происходит, заключается в том, что когда вы вызываете curriedSum , вы фактически получаете два традиционных вызова функций друг за другом. Первая функция вызов принимает один параметр Int с именем x и возвращает функцию значение для второй функции. Эта вторая функция принимает параметр Int <Код> у .

Здесь есть функция с именем first , которая по духу делает то, что первый традиционный вызов функции curriedSum :

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

Применение 1 к первой функции & # 8212; другими словами, вызов первой функции и передача в 1 & # 8212; возвращает вторую функцию:

scala> val second = first(1)
second: (Int) => Int = <function1>

Применение 2 ко второй функции дает результат:

scala> second(2)
res6: Int = 3

Примером каррирования может служить ситуация, когда у вас есть функции, которые в данный момент знают только один из параметров:

Например:

func aFunction(str: String) {
    let callback = callback(str) // signature now is `NSData -> ()`
    performAsyncRequest(callback)
}

func callback(str: String, data: NSData) {
    // Callback code
}

func performAsyncRequest(callback: NSData -> ()) {
    // Async code that will call callback with NSData as parameter
}

Здесь, так как вы не знаете второй параметр для обратного вызова при отправке его в executeAsyncRequest (_:) , вам потребуется создать еще одну лямбда / замыкание, чтобы отправить его в функцию.

Как и все остальные ответы, карри помогает создавать частично примененные функции. Javascript не предоставляет встроенную поддержку автоматического карри. Поэтому приведенные выше примеры могут не помочь в практическом кодировании. В LifeScript есть отличный пример (который по сути компилируется в js) http://livescript.net/

times = (x, y) --> x * y
times 2, 3       #=> 6 (normal use works as expected)
double = times 2
double 5         #=> 10

В приведенном выше примере, когда вы указали меньше аргументов, liveScript генерирует для вас новую каррированную функцию (double)

Здесь вы можете найти простое объяснение реализации карри в C #. В комментариях я попытался показать, как карри может быть полезным:

public static class FuncExtensions {
    public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
    {
        return x1 => x2 => func(x1, x2);
    }
}

//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);

//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times 
//with different input parameters.

int result = func(1);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top