Является ли раздел результатом каррирования?
В программировании в Хаскеле Хаттоном
В общем, если
#
является оператором, то выражения вида(#)
,(x #)
, а также(# y)
для аргументовx
а такжеy
называются разделами, значение которых как функции можно формализовать с помощью лямбда-выражений следующим образом:(#) = \x -> (\y -> x # y) (x #) = \y -> x # y (# y) = \x -> x # y
В чем разница между "разделом" и "карри"?
Является ли раздел результатом применения операции каррирования к функции с несколькими аргументами?
Благодарю.
3 ответа
Левые и правые секции являются синтаксическими устройствами для частичного применения инфиксного оператора к одному аргументу (см. Также ответ Чепнера). Для точности отметим, что карри - это не то же самое, что частичное применение:
Карринг - это преобразование функции, которая принимает N аргументов, в функцию, которая принимает один аргумент и возвращает функцию, которая принимает N-1 аргументы.
Частичное приложение создает функцию, которая принимает N-1 аргументов из функции, которая принимает N аргументов, предоставляя один из аргументов.
В Хаскеле бывает, что все карри; все функции принимают только один аргумент (даже неиспользуемые функции в Haskell принимают кортеж, который, строго говоря, является единственным аргументом - вы можете поиграть с curry
а также uncurry
функции, чтобы увидеть, как это работает). Тем не менее, мы очень часто неофициально думаем о функциях, которые возвращают функции как функции нескольких аргументов. С этой точки зрения, приятным следствием каррирования по умолчанию является то, что частичное применение функции к первому аргументу становится тривиальным: в то время как, например, elem
принимает значение и контейнер и проверяет, является ли значение элементом контактера, elem "apple"
берет контейнер (из строк) и проверяет, "apple"
является элементом этого.
Что касается операторов, когда мы пишем, например...
5 / 2
... мы подаем заявку на оператора /
к аргументам 5
а также 2
, Оператор также может использоваться в префиксной форме, а не в инфиксной:
(/) 5 2
В префиксной форме оператор может быть частично применен обычным способом:
(/) 5
Это, однако, возможно, выглядит немного неловко - в конце концов, 5
здесь числитель, а не знаменатель. Я бы сказал, что в этом случае синтаксис левого раздела проще для глаз:
(5 /)
Кроме того, частичное применение ко второму аргументу не так просто написать, требуя лямбда или flip
, В случае операторов, правильный раздел может помочь с этим:
(/ 2)
Обратите внимание, что разделы также работают с функциями, превращенными в операторы через синтаксис backtick, так что...
(`elem` ["apple", "grape", "orange"])
... берет строку и проверяет, можно ли ее найти в ["apple", "grape", "orange"]
,
Раздел - это просто специальный синтаксис для применения инфиксного оператора к одному аргументу. (# y)
является более полезным из двух, так как (x #)
эквивалентно (#) x
(который просто применяет инфиксный оператор как функцию к одному аргументу обычным способом).
curry f x y = f (x,y)
, uncurry g (x,y) = g x y
,
(+ 3) 4 = (+) 4 3 = 4 + 3
, (4 +) 3 = (+) 4 3 = 4 + 3
,
Раздел является результатом частичного применения карри функции: (+ 3) = flip (+) 3
, (4 +) = (+) 4
,
Функция карри (как g
или же (+)
) ожидает своих аргументов по одному. Некурри функция (как f
) ожидает своих аргументов в кортеже.
Чтобы частично применить некритированную функцию, мы должны сначала превратить ее в каррированную функцию с curry
, Чтобы частично применить карри функцию, нам ничего не нужно делать, просто примените ее к аргументу.
curry :: ((a, b) -> c ) -> ( a -> (b -> c))
uncurry :: (a -> (b -> c)) -> ((a, b) -> c )
x :: a
g :: a -> (b -> c)
--------------------
g x :: b -> c
x :: a
f :: (a, b) -> c
---------------------------
curry f :: a -> (b -> c)
curry f x :: b -> c