Что такое «карри»? - PullRequest
       95

Что такое «карри»?

585 голосов
/ 31 августа 2008

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

Ответы [ 15 ]

771 голосов
/ 31 августа 2008

Каррирование - это когда вы разбиваете функцию, которая принимает несколько аргументов, на ряд функций, которые принимают часть аргументов. Вот пример в 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.

119 голосов
/ 30 августа 2009

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

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

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

91 голосов
/ 30 августа 2009

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

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

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

46 голосов
/ 31 августа 2008

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

Например, в 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

Где 'fun 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.

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

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

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

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
32 голосов
/ 14 января 2016

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

В 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...
28 голосов
/ 19 октября 2008

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

6 голосов
/ 30 августа 2009

Вот пример игрушки на 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/library/functools.html?highlight=partial#functools.partial, который также показывает частичное различие между объектом и функцией в том, как Python реализует это.

4 голосов
/ 26 июня 2015

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

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

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

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

(inc 7) # => 8

Давайте сделаем это сами, используя partial:

(def inc (partial add 1))

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

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

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

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

3 голосов
/ 26 марта 2019

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

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

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

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

Давайте сделаем функцию карри, которая выполняет каррирование для функций с двумя аргументами. Другими словами, 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, и передает вызов исходной сумме с несколькими аргументами.
3 голосов
/ 21 июня 2018

Карри может упростить ваш код. Это одна из главных причин использовать это. Карринг - это процесс преобразования функции, которая принимает 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);

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

...