В чем разница между карри и частичным применением? - PullRequest
394 голосов
/ 20 октября 2008

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

Я не нашел достойного объяснения того, что такое частичное приложение или чем оно отличается от карри. Похоже, что существует общая путаница с эквивалентными примерами, описываемыми как карри в одних местах и ​​частичное применение в других.

Может ли кто-нибудь дать мне определение как терминов, так и деталей их различия?

Ответы [ 14 ]

2 голосов
/ 13 мая 2013

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

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

1 голос
/ 28 февраля 2017

Здесь есть и другие замечательные ответы, но я считаю, что этот пример (согласно моему пониманию) на Java может быть полезным для некоторых людей:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  A, Function< B, X >  > curry( BiFunction< A, B, X > bif ){
    return a -> partiallyApply( bif, a );
}

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

Если вы хотите копировать и вставлять, ниже будет шумнее, но удобнее работать, поскольку типы более мягкие:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  ? super A,  Function< ? super B, ? extends X >  > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
    return a -> partiallyApply( bif, a );
}
0 голосов
/ 12 мая 2019

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

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

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

Curry - это когда вы определяете функцию.

Частичное применение - это когда вы вызываете функцию.

Приложение - математический язык для вызова функции.

Частичное приложение требует вызова карри-функции и получения функции в качестве возвращаемого типа.

0 голосов
/ 09 апреля 2014

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

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

(+) :: Int -> Int -> Int

Теперь, как вы превращаете это в функцию, которая принимает один аргумент? Вы обманываете, конечно!

plus :: (Int, Int) -> Int

Обратите внимание, что плюс теперь принимает один аргумент (который состоит из двух вещей). Супер!

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

(uncurry (+)) (1,2)

Так что же такое частичное применение функции? Это другой способ превратить функцию с двумя аргументами в функцию с одним аргументом. Это работает по-другому, хотя. Опять же, давайте возьмем (+) в качестве примера. Как мы можем превратить его в функцию, которая принимает один Int в качестве аргумента? Мы обманываем!

((+) 0) :: Int -> Int

Это функция, которая добавляет ноль к любому Int.

((+) 1) :: Int -> Int

добавляет 1 к любому Int. И т. Д. В каждом из этих случаев (+) «частично применяется».

...