Применение функции в 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 отличаются из-за разделов.

Другие вопросы по тегам