Применение функции в Haskell
Хорошо, это был долгий день, и мой мозг может не функционировать на уровне Haskell, но я просто не могу понять один пример из "Learn You a Haskell".
Раздел называется Application Function с $, и есть пример того, как $
может быть определено:
($) :: (a -> b) -> a -> b
f $ x = f x
Пока все ясно. Я понимаю все примеры в разделе, кроме последнего:
ghci> map ($ 3) [(4+), (10*), (^2), sqrt]
[7.0,30.0,9.0,1.7320508075688772]
Здесь мы на карте ($ 3)
через список функций и получить результат применения этих функций к 3
, Но как это возможно?
Из первого фрагмента кода ясно, что первый аргумент - это функция, которую мы можем даже написать:
*Main> ($) sqrt 4
2.0
Сейчас ($ 3)
это частичное применение функции $
, но 3
идет на позицию функции! Так 3
должна быть функция или как?
Есть еще одна загадка: что за хрень (4+)
? я знаю это (+4)
это частичное применение функции +
, так (4+)
должно быть частичное применение функции 4
? Ерунда. Какой трюк работает здесь?
3 ответа
($ 3)
а также (+ 4)
это не частичные приложения - это операторские секции. Частичное заявление будет выглядеть (($) 3)
или же ((+) 4)
,
Операторская часть формы (? x)
(где ?
обозначает произвольный инфиксный оператор) связывает правый операнд оператора, т.е. он эквивалентен \y -> y ? x
, Аналогично раздел оператора (x ?)
связывает левый операнд и, таким образом, эквивалентно частичному применению.
Я думаю, что сбивает вас с толку, это операторские секции. Они позволяют частично применить оператор с любым из его аргументов, поэтому вы можете иметь операторы (+4)
а также (4+)
, где 4
является вторым, то первый аргумент +
соответственно. Более ясный пример может быть ("Hello" ++)
против (++ "world")
, бывший претендует "Hello"
на передней части строки, в то время как последний добавляет "world"
на конец строки.
Это контрастирует с использованием операторов в префиксной форме с просто паренами вокруг него. В этой форме следующее эквивалентно:
> let join = (++)
> join "Hello, " "world"
"Hello, world"
> (++) "Hello, " "world"
"Hello, world"
В префиксной форме вы рассматриваете оператор как обычную функцию, и он принимает первый, а затем второй аргумент по порядку. В разделах оператора имеет значение, на какой стороне оператора находится аргумент.
Так что в вашем примере у вас есть частичное применение ($ 3)
Вы можете уменьшить его как
map ($ 3) [(4+), (10*), (^2), sqrt]
[($ 3) (4+), ($ 3) (10 *), ($ 3) (^ 2), ($ 3) sqrt]
[4 + 3, 10 * 3, 3 ^ 2, sqrt 3]
[7.0, 30.0, 9.0, 1.7320508075688772]
Вы путаетесь с разделами. Хороший способ понять концепцию разделов - поиграть с примером:
(<^>) :: Int -> Float -> Int
a <^> b = a
Вышеуказанная функция является бесполезной функцией, которая возвращает первый параметр независимо от того, что является вторым параметром. Но он принимает Int
а потом Float
в качестве ввода.
Теперь из-за разделов вы можете применять любой из их аргументов:
λ> let a = (3 <^>)
λ> :t a
a :: Float -> Int
λ> let b = (<^> 3.0)
λ> :t b
b :: Int -> Int
Посмотрите, как тип a
а также b
отличаются из-за разделов.