Маленький пример на тему практической пользы возможности частичного применения функций.
Предположим, что имеется некоторая функция, выводящая приветствие:
printHello::(String->String)->String->String
printHello f x = "Hello, " ++ f x ++ "!"
Поприветствуем Васю:
ghci> printHello (\x->x) "Vasya"
"Hello, Vasya!"
shortName::String->String->String->String
shortName [] _ xs = xs
shortName (x:_) [] xs = x : ". " ++ xs
shortName (x:_) (y:_) xs = x : '.' : y : ". " ++ xs
ghci> printHello (\x->x) $ shortName "Vasiliy" "Vasilievich" "Vasiliev"
"Hello, V.V. Vasiliev!"
Однако, благодаря возможности частичного применения функций, мы спокойно можем передать shortName первым параметром с двумя аргументами вместо трёх (третий [фамилия] будет передан ей функцией printHello):
ghci> let n = shortName "Vasiliy" "Vasilievich"
ghci> printHello n "Vasiliev"
"Hello, V.V. Vasiliev!"
или так:
ghci> printHello (shortName "Vasiliy" "Vasilievich") "Vasiliev"
"Hello, V.V. Vasiliev!"
Поскольку в данном случае в shortName передаётся два параметра вместо трёх, то генерируется промежуточная функция, сигнатура которой совпадает с ожидаемой. Этой сгенерированной функции printHello передаёт в качестве параметра фамилию, что собственно и приводит к запуску функции shortName. В результате получаем ожидаемую строку.
Вывод
Возможность частичного применения функций может представлять интерес тем, что позволяет нам "прозрачно" передавать и использовать функции, имеющие сигнатуру отличную от ожидаемой. За счёт этого код получается более кратким, т.к. не нужно создавать дополнительные варианты функций под различные требующиеся сигнатуры.
Предположим, что имеется некоторая функция, выводящая приветствие:
printHello::(String->String)->String->String
printHello f x = "Hello, " ++ f x ++ "!"
Первым параметром функции указывается функция предварительной обработки передаваемой строки. Если обработка не нужна, то можно передавать заглушку в виде лямбда выражения, которое просто возвращает полученную строку без каких-либо изменений.
ghci> printHello (\x->x) "Vasya"
"Hello, Vasya!"
А что если мы захотим поприветствовать Васю более официально: с указанием его инициалов и фамилии? Пусть у нас даже имеется специальная функция для формирования нужной строки:
shortName::String->String->String->String
shortName [] _ xs = xs
shortName (x:_) [] xs = x : ". " ++ xs
shortName (x:_) (y:_) xs = x : '.' : y : ". " ++ xs
Параметры в shortName ожидают последовательно имя, отчество и фамилию, на основании которых и будет формироваться конечная строка. Попробуем функцию в деле:
ghci> shortName "Vasiliy" "Vasilievich" "Vasiliev"
"V.V. Vasiliev"
ghci> shortName "Vasiliy" "" "Vasiliev"
"V. Vasiliev"
ghci> shortName "" "Vasilievich" "Vasiliev"
"Vasiliev"
ghci> shortName "" "" "Vasiliev"
"Vasiliev"
Заметьте, сигнатура функции shortName отличается от сигнатуры той функции, которая первым параметром передаётся в printHello. В виду этого мы могли бы снова прибегнуть к помощи лямбда-выражения и использовать shortName при вызове printHello например так:
ghci> printHello (\x->x) $ shortName "Vasiliy" "Vasilievich" "Vasiliev"
"Hello, V.V. Vasiliev!"
Однако, благодаря возможности частичного применения функций, мы спокойно можем передать shortName первым параметром с двумя аргументами вместо трёх (третий [фамилия] будет передан ей функцией printHello):
ghci> let n = shortName "Vasiliy" "Vasilievich"
ghci> printHello n "Vasiliev"
"Hello, V.V. Vasiliev!"
или так:
ghci> printHello (shortName "Vasiliy" "Vasilievich") "Vasiliev"
"Hello, V.V. Vasiliev!"
Поскольку в данном случае в shortName передаётся два параметра вместо трёх, то генерируется промежуточная функция, сигнатура которой совпадает с ожидаемой. Этой сгенерированной функции printHello передаёт в качестве параметра фамилию, что собственно и приводит к запуску функции shortName. В результате получаем ожидаемую строку.
Вывод
Возможность частичного применения функций может представлять интерес тем, что позволяет нам "прозрачно" передавать и использовать функции, имеющие сигнатуру отличную от ожидаемой. За счёт этого код получается более кратким, т.к. не нужно создавать дополнительные варианты функций под различные требующиеся сигнатуры.
Комментариев нет:
Отправить комментарий