Яка різниця між крапкою (.)та знаком долара ($)?
Як я розумію, вони обидва синтаксичного цукру, оскільки не потрібно використовувати дужки.
Яка різниця між крапкою (.)та знаком долара ($)?
Як я розумію, вони обидва синтаксичного цукру, оскільки не потрібно використовувати дужки.
Відповіді:
$Оператор для уникнення круглих дужок. Все, що з’явиться після нього, матиме перевагу перед усім, що трапляється раніше.
Наприклад, скажімо, у вас є рядок, який гласить:
putStrLn (show (1 + 1))
Якщо ви хочете позбутися цих скобок, будь-який з наступних рядків також зробив би те саме:
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
Основна мета .оператора - не уникати дужок, а ланцюгових функцій. Це дозволяє прив’язати висновок того, що з’являється праворуч, на вході того, що з’являється зліва. Зазвичай це також призводить до меншої кількості дужок, але працює інакше.
Повернення до того ж прикладу:
putStrLn (show (1 + 1))
(1 + 1)не має введення, і тому його не можна використовувати з .оператором.showможе взяти Intі повернути a String.putStrLnможе взяти Stringі повернути IO ().Ви можете ланцюга , showщоб putStrLnце подобається:
(putStrLn . show) (1 + 1)
Якщо занадто багато дужок на ваш смак, позбавтеся їх від $оператора:
putStrLn . show $ 1 + 1
putStrLn . show . (+1) $ 1було б рівнозначним. Ви вірні в тому, що більшість (усіх?) Операторів інфіксів - це функції.
map ($3). Я маю на увазі, я в основному також використовую, $щоб уникнути дужок, але це не так, як це все для них.
map ($3)є функцією типу Num a => [(a->b)] -> [b]. Він займає список функцій, що приймають число, застосовує 3 до всіх і збирає результати.
Вони мають різні типи та різні визначення:
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 все, окрім остаточного, можна замінити.
xбула функція? Чи можете ви потім використовувати .як остаточний?
xв цьому контексті, то так - але тоді "остаточний" буде застосований до чогось іншого, ніж x. Якщо ви не подаєте заявку x, то це не відрізняється від xзначення.
Також зверніть увагу , що ($)це функція особистості спеціалізується на типи функцій . Функція ідентичності виглядає приблизно так:
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)
Сподіваюся, це допомагає!
($) дозволяє функції зв'язати між собою без додавання дужок для контролю порядку оцінки:
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"
Один додаток, який корисний і зайняв у мене деякий час, щоб зрозуміти, з дуже короткого опису, щоб дізнатись про це 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) .... Очевидно, останні варіанти будуть більш читабельні для більшості людей.
$3без місця. Якщо ввімкнено шаблон Haskell, це буде розбиратися як сплайс, тоді як $ 3завжди означає те, що ви сказали. Загалом, здається, у Haskell є тенденція до "крадіжки" біт синтаксису, наполягаючи на тому, що певні оператори мають пробіли навколо них, щоб їх можна розглядати як такі.
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
коли ми цього не робимо.
... або ви могли уникнути .і $конструкцій, використовуючи трубопроводи :
third xs = xs |> tail |> tail |> head
Це після того, як ви додали функцію помічника:
(|>) x y = y x
$оператор Haskell насправді працює як F #, <|ніж це |>, як правило, в haskell ви б написали вищевказану функцію так: third xs = head $ tail $ tail $ xsабо, можливо, навіть як third = head . tail . tail, яка в синтаксисі F # -style була б приблизно такою:let third = List.head << List.tail << List.tail
Прекрасний спосіб дізнатися більше про що-небудь (будь-яку функцію) - пам’ятати, що все - це функція! Ця загальна мантра допомагає, але в конкретних випадках, таких як оператори, вона допомагає запам'ятати цю маленьку хитрість:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
і
:t ($)
($) :: (a -> b) -> a -> b
Просто пам’ятайте, що ви користуєтеся ними в :tрозкішних цілях , і оберігайте своїх операторів ()!
Моє правило просте (я і початківець):
. якщо ви хочете передати параметр (викликати функцію), і$якщо ще немає параметра (складіть функцію)Це є
show $ head [1, 2]
але ніколи:
show . head [1, 2]
Я думаю, короткий приклад того, де ви б використали, .і не $допоможе з’ясувати речі.
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
Зауважте, що times6це функція, яка створена із складу функції.
Всі інші відповіді досить хороші. Але є важлива детальна інформація про те, як ghc поводиться з $, що перевірка типу ghc дозволяє встановити тип з більш високим рангом / кількісно визначеними типами. Якщо ви подивитесь на тип типу, $ idнаприклад, ви побачите, що він візьме функцію, аргумент якої є самою поліморфною функцією. Такі дрібниці не дають однакової гнучкості з еквівалентними операторами. (Це насправді змушує мене замислитись, заслуговує ли $! Чи ні)?