Яка різниця між крапкою (.)
та знаком долара ($)
?
Як я розумію, вони обидва синтаксичного цукру, оскільки не потрібно використовувати дужки.
Яка різниця між крапкою (.)
та знаком долара ($)
?
Як я розумію, вони обидва синтаксичного цукру, оскільки не потрібно використовувати дужки.
Відповіді:
$
Оператор для уникнення круглих дужок. Все, що з’явиться після нього, матиме перевагу перед усім, що трапляється раніше.
Наприклад, скажімо, у вас є рядок, який гласить:
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
наприклад, ви побачите, що він візьме функцію, аргумент якої є самою поліморфною функцією. Такі дрібниці не дають однакової гнучкості з еквівалентними операторами. (Це насправді змушує мене замислитись, заслуговує ли $! Чи ні)?