Яка різниця між . (крапка) і $ (знак долара)?


709

Яка різниця між крапкою (.)та знаком долара ($)?

Як я розумію, вони обидва синтаксичного цукру, оскільки не потрібно використовувати дужки.

Відповіді:


1226

$Оператор для уникнення круглих дужок. Все, що з’явиться після нього, матиме перевагу перед усім, що трапляється раніше.

Наприклад, скажімо, у вас є рядок, який гласить:

putStrLn (show (1 + 1))

Якщо ви хочете позбутися цих скобок, будь-який з наступних рядків також зробив би те саме:

putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1

Основна мета .оператора - не уникати дужок, а ланцюгових функцій. Це дозволяє прив’язати висновок того, що з’являється праворуч, на вході того, що з’являється зліва. Зазвичай це також призводить до меншої кількості дужок, але працює інакше.

Повернення до того ж прикладу:

putStrLn (show (1 + 1))
  1. (1 + 1)не має введення, і тому його не можна використовувати з .оператором.
  2. showможе взяти Intі повернути a String.
  3. putStrLnможе взяти Stringі повернути IO ().

Ви можете ланцюга , showщоб putStrLnце подобається:

(putStrLn . show) (1 + 1)

Якщо занадто багато дужок на ваш смак, позбавтеся їх від $оператора:

putStrLn . show $ 1 + 1

54
Насправді, оскільки + також є функцією, ви не могли зробити її префіксом, а потім складіть її, як `putStrLn. шоу. (+) 1 1 `Не те, щоб це було зрозуміліше, але я маю на увазі ... ти міг би, правда?
CodexArcanum

4
@CodexArcanum У цьому прикладі щось подібне putStrLn . show . (+1) $ 1було б рівнозначним. Ви вірні в тому, що більшість (усіх?) Операторів інфіксів - це функції.
Майкл Стіл

79
Цікаво, чому ніхто ніколи не згадує подібне використання map ($3). Я маю на увазі, я в основному також використовую, $щоб уникнути дужок, але це не так, як це все для них.
Кубік

43
map ($3)є функцією типу Num a => [(a->b)] -> [b]. Він займає список функцій, що приймають число, застосовує 3 до всіх і збирає результати.
Cubic

21
Ви повинні бути обережними при використанні $ з іншими операторами. "x + f (y + z)" не те саме, що "x + f $ y + z", оскільки останній насправді означає "(x + f) (y + z)" (тобто сума x і f дорівнює трактується як функція).
Пол Джонсон

186

Вони мають різні типи та різні визначення:

infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)

infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x

($)призначений для заміни звичайних функціональних додатків, але з іншим пріоритетом, щоб уникнути дужок. (.)призначений для складання двох функцій разом для створення нової функції.

У деяких випадках вони взаємозамінні, але це взагалі не вірно. Типовий приклад, де вони є:

f $ g $ h $ x

==>

f . g . h $ x

Іншими словами, у ланцюжку $s все, окрім остаточного, можна замінити.


1
Що робити, якщо xбула функція? Чи можете ви потім використовувати .як остаточний?
richizy

3
@richizy, якщо ви насправді застосовуєте xв цьому контексті, то так - але тоді "остаточний" буде застосований до чогось іншого, ніж x. Якщо ви не подаєте заявку x, то це не відрізняється від xзначення.
GS - Вибачте Моніку

124

Також зверніть увагу , що ($)це функція особистості спеціалізується на типи функцій . Функція ідентичності виглядає приблизно так:

id :: a -> a
id x = x

Хоча це ($)виглядає так:

($) :: (a -> b) -> (a -> b)
($) = id

Зауважте, що у підписі типу я навмисно додав додаткові дужки.

Використання ($)зазвичай може бути усунена шляхом додавання дужок (якщо оператор не використовується в секції). Напр .: f $ g xстає f (g x).

Використання (.)часто замінити трохи складніше; їм зазвичай потрібна лямбда або введення явного параметра функції. Наприклад:

f = g . h

стає

f x = (g . h) x

стає

f x = g (h x)

Сподіваюся, це допомагає!


"Зверніть увагу, що я навмисно додав додаткові дужки в підпис типу." Я розгублений ... чому ти це зробив?
Mateen Ulhaq

3
@MateenUlhaq Тип ($) - це (a -> b) -> a -> b, що таке саме, як (a -> b) -> (a -> b), але додаткові дужки тут додають деякі ясність.
Руді

2
О, я гадаю. Я думав про це як про функції двох аргументів ... але через curry, це рівнозначно функції, яка повертає функцію.
Матін Ульхак

78

($) дозволяє функції зв'язати між собою без додавання дужок для контролю порядку оцінки:

Prelude> head (tail "asdf")
's'

Prelude> head $ tail "asdf"
's'

Оператор композиції (.)створює нову функцію без вказівки аргументів:

Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'

Prelude> let second = head . tail
Prelude> second "asdf"
's'

Наведений вище приклад, мабуть, ілюстративний, але насправді не показує зручності використання композиції. Ось ще одна аналогія:

Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"

Якщо ми використовуємо лише один раз третій, ми можемо уникати називати його за допомогою лямбда:

Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"

Нарешті, композиція дозволяє нам уникати лямбда:

Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"

3
Якщо stackoverflow мав комбіновану функцію, я вважаю за краще відповідь, що поєднує попередні два пояснення з прикладом у цій відповіді.
Chris.Q

59

Коротка та солодка версія:

  • ($) викликає функцію, яка є її лівим аргументом, на значення, яке є його правою аргументом.
  • (.) складає функцію, яка є її лівим аргументом, на функцію, яка є її правою аргументом.

29

Один додаток, який корисний і зайняв у мене деякий час, щоб зрозуміти, з дуже короткого опису, щоб дізнатись про це haskell : Since:

f $ x = f x

і в дужках правою частиною виразу, що містить оператор інфіксації, перетворюється його у функцію префікса, можна записати ($ 3) (4+)аналог (++", world") "hello".

Чому хтось би це робив? Наприклад, для списків функцій. Обидва:

map (++", world") ["hello","goodbye"]`

і:

map ($ 3) [(4+),(3*)]

коротші map (\x -> x ++ ", world") ...або map (\f -> f 3) .... Очевидно, останні варіанти будуть більш читабельні для більшості людей.


14
btw, я б радив не використовувати $3без місця. Якщо ввімкнено шаблон Haskell, це буде розбиратися як сплайс, тоді як $ 3завжди означає те, що ви сказали. Загалом, здається, у Haskell є тенденція до "крадіжки" біт синтаксису, наполягаючи на тому, що певні оператори мають пробіли навколо них, щоб їх можна розглядати як такі.
GS - Вибачте Моніку

1
Зайняв мене час, щоб зрозуміти, як працюють круглі дужки: en.wikibooks.org/wiki/Haskell/…
Casebash

18

Haskell: різниця між .(крапка) та $(знак долара)

Яка різниця між крапкою (.)та знаком долара ($)?. Як я розумію, вони обидва синтаксичного цукру, оскільки не потрібно використовувати дужки.

Вони не є синтаксичним цукром для не потребувати використання дужок - вони є функціями, - фіксованими, тому ми можемо називати їх операторами.

Складіть, (.)і коли його використовувати.

(.)- композитна функція. Тому

result = (f . g) x

те саме, що побудувати функцію, яка передає результат свого аргументу, переданого gна f.

h = \x -> f (g x)
result = h x

Використовуйте, (.)коли у вас немає аргументів для переходу до функцій, які ви хочете скласти.

Право асоціативне застосовується, ($)і коли його використовувати

($)є право-асоціативною функцією застосування з низьким пріоритетом зв'язування. Отже, він просто обчислює речі спочатку праворуч від нього. Таким чином,

result = f $ g x

те саме, що це, процедурно (що важливо, оскільки Хаскелл оцінюється ліниво, він почне оцінювати fспочатку):

h = f
g_x = g x
result = h g_x

або більш стисло:

result = f (g x)

Використовуйте, ($)коли у вас є всі змінні для оцінки, перш ніж застосувати попередню функцію до результату.

Ми можемо побачити це, прочитавши джерело для кожної функції.

Прочитайте Джерело

Ось джерело для (.):

-- | Function composition.
{-# INLINE (.) #-}
-- Make sure it has TWO args only on the left, so that it inlines
-- when applied to two functions, even if there is no final argument
(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

А ось джерело для ($):

-- | Application operator.  This operator is redundant, since ordinary
-- application @(f x)@ means the same as @(f '$' x)@. However, '$' has
-- low, right-associative binding precedence, so it sometimes allows
-- parentheses to be omitted; for example:
--
-- >     f $ g $ h x  =  f (g (h x))
--
-- It is also useful in higher-order situations, such as @'map' ('$' 0) xs@,
-- or @'Data.List.zipWith' ('$') fs xs@.
{-# INLINE ($) #-}
($)                     :: (a -> b) -> a -> b
f $ x                   =  f x

Висновок

Використовуйте композицію, коли вам не потрібно негайно оцінювати функцію. Можливо, ви хочете передати функцію, що є результатом композиції, в іншу функцію.

Використовуйте додаток, коли ви надаєте всі аргументи для повної оцінки.

Тож для нашого прикладу це було б семантично бажано робити

f $ g x

коли ми маємо x(вірніше, gаргументи), і робимо:

f . g

коли ми цього не робимо.


12

... або ви могли уникнути .і $конструкцій, використовуючи трубопроводи :

third xs = xs |> tail |> tail |> head

Це після того, як ви додали функцію помічника:

(|>) x y = y x

2
Так, |> - оператор трубопроводу F #.
користувач1721780

6
Тут слід зазначити, що $оператор Haskell насправді працює як F #, <|ніж це |>, як правило, в haskell ви б написали вищевказану функцію так: third xs = head $ tail $ tail $ xsабо, можливо, навіть як third = head . tail . tail, яка в синтаксисі F # -style була б приблизно такою:let third = List.head << List.tail << List.tail
Електрична кава

1
Навіщо додавати функцію помічника, щоб Haskell виглядав як F #? -1
вікінгстев

9
Перевернутий $уже доступний, і він називається & hackage.haskell.org/package/base-4.8.0.0/docs/…
пат

11

Прекрасний спосіб дізнатися більше про що-небудь (будь-яку функцію) - пам’ятати, що все - це функція! Ця загальна мантра допомагає, але в конкретних випадках, таких як оператори, вона допомагає запам'ятати цю маленьку хитрість:

:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

і

:t ($)
($) :: (a -> b) -> a -> b

Просто пам’ятайте, що ви користуєтеся ними в :tрозкішних цілях , і оберігайте своїх операторів ()!


11

Моє правило просте (я і початківець):

  • не використовувати . якщо ви хочете передати параметр (викликати функцію), і
  • не використовувати, $якщо ще немає параметра (складіть функцію)

Це є

show $ head [1, 2]

але ніколи:

show . head [1, 2]

3
Гарна евристика, але можна використати більше прикладів
Zoey Hewll

0

Я думаю, короткий приклад того, де ви б використали, .і не $допоможе з’ясувати речі.

double x = x * 2
triple x = x * 3
times6 = double . triple

:i times6
times6 :: Num c => c -> c

Зауважте, що times6це функція, яка створена із складу функції.


0

Всі інші відповіді досить хороші. Але є важлива детальна інформація про те, як ghc поводиться з $, що перевірка типу ghc дозволяє встановити тип з більш високим рангом / кількісно визначеними типами. Якщо ви подивитесь на тип типу, $ idнаприклад, ви побачите, що він візьме функцію, аргумент якої є самою поліморфною функцією. Такі дрібниці не дають однакової гнучкості з еквівалентними операторами. (Це насправді змушує мене замислитись, заслуговує ли $! Чи ні)?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.