Haskell Language
Частичное применение
Поиск…
замечания
Давайте проясним некоторые заблуждения, которые могут сделать новички.
Возможно, вы столкнулись с такими функциями, как:
max :: (Ord a) => a -> a -> a
max m n
| m >= n = m
| otherwise = n
Начинающие обычно рассматривают max :: (Ord a) => a -> a -> a
as, которая принимает два аргумента (значения) типа a
и возвращает значение типа a
. Однако то, что действительно происходит, заключается в том, что max
принимает один аргумент типа a
и возвращает функцию типа a -> a
. Затем эта функция принимает аргумент типа a
и возвращает окончательное значение типа a
.
Действительно, max
можно записать как max :: (Ord a) => a -> (a -> a)
Рассмотрим сигнатуру типа max
:
Prelude> :t max
max :: Ord a => a -> a -> a
Prelude> :t (max 75)
(max 75) :: (Num a, Ord a) => a -> a
Prelude> :t (max "Fury Road")
(max "Fury Road") :: [Char] -> [Char]
Prelude> :t (max "Fury Road" "Furiosa")
(max "Fury Road" "Furiosa") :: [Char]
max 75
и max "Fury Road"
могут не выглядеть как функции, но на самом деле они есть.
Путаница проистекает из того факта, что в математике и многих других обычных языках программирования нам разрешено иметь функции, которые принимают несколько аргументов. Однако в Haskell функции могут принимать только один аргумент, и они могут возвращать либо значения, такие как a
, либо такие функции, как a -> a
.
Частично примененная функция добавления
Мы можем использовать частичное приложение для «блокировки» первого аргумента. После применения одного аргумента мы остаемся с функцией, которая ожидает еще один аргумент перед возвратом результата.
(+) :: Int -> Int -> Int
addOne :: Int -> Int
addOne = (+) 1
Затем мы можем использовать addOne
, чтобы добавить его в Int
.
> addOne 5
6
> map addOne [1,2,3]
[2,3,4]
Возврат частично прикладной функции
Возвращение частично применяемых функций - это один из способов записи сжатого кода.
add :: Int -> Int -> Int
add x = (+x)
add 5 2
В этом примере (+ x) - частично применяемая функция. Обратите внимание, что второй параметр функции добавления необязательно указывать в определении функции.
Результатом вызова add 5 2
составляет семь.
Разделы
Разделение является кратким способом частичного применения аргументов к инфиксным операторам.
Например, если мы хотим написать функцию, которая добавляет «ing» в конец слова, мы можем использовать раздел, чтобы кратко определить функцию.
> (++ "ing") "laugh"
"laughing"
Обратите внимание, как мы частично применили второй аргумент. Обычно мы можем лишь частично применять аргументы в указанном порядке.
Мы также можем использовать левую секцию для частичного применения первого аргумента.
> ("re" ++) "do"
"redo"
Мы могли бы эквивалентно написать это, используя обычное префиксное частное приложение:
> ((++) "re") "do"
"redo"
Примечание по вычитанию
Начинающие часто неправильно разделяют отрицание.
> map (-1) [1,2,3]
***error: Could not deduce...
Это не работает, поскольку -1
анализируется как литерал -1
а не секционированный оператор -
применяется к 1
. Функция subtract
существует, чтобы обойти эту проблему.
> map (subtract 1) [1,2,3]
[0,1,2]