В чем разница между. (точка) и $ (знак доллара)?
В чем разница между точкой (.)
и знаком доллара ($)
?
Насколько я понимаю, они оба являются синтаксическим сахаром для того, чтобы не использовать скобки.
Ответы
Ответ 1
Оператор $
предназначен для исключения скобок. Все, что появляется после него, будет иметь приоритет над всем, что было раньше.
Например, скажем, у вас есть строка, которая гласит:
putStrLn (show (1 + 1))
Если вы хотите избавиться от этих круглых скобок, любая из следующих строк также сделает то же самое:
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
Основная цель оператора .
- не избегать скобок, а цепочки. Это позволяет вам привязывать вывод того, что появляется справа от входа любого, что появляется слева. Обычно это также приводит к меньшему количеству скобок, но работает по-разному.
Возвращаясь к тому же примеру:
putStrLn (show (1 + 1))
-
(1 + 1)
не имеет ввода и поэтому не может использоваться с оператором .
.
-
show
может принимать Int
и возвращать String
.
-
putStrLn
может принимать String
и возвращать IO ()
.
Вы можете привязать show
к putStrLn
следующим образом:
(putStrLn . show) (1 + 1)
Если слишком много скобок по вашему вкусу, избавьтесь от них с помощью оператора $
:
putStrLn . show $ 1 + 1
Ответ 2
У них разные типы и разные определения:
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 все, кроме окончательного, можно заменить на .
Ответ 3
Также обратите внимание, что ($)
- это функция идентификации, специализированная для типов функций. Функция идентификации выглядит следующим образом:
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)
Надеюсь, это поможет!
Ответ 4
($)
позволяет комбинировать функции вместе без добавления скобок для управления порядком оценки:
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"
Ответ 5
Короткий и сладкий вариант:
-
($)
вызывает функцию, которая является ее левым аргументом для значения, которое является его правым аргументом.
-
(.)
составляет функцию, которая является ее левым аргументом для функции, которая является ее правым аргументом.
Ответ 6
Одно приложение, которое полезно, и взяло у меня некоторое время, чтобы выяснить из очень короткого описания узнав о вас haskell: Поскольку:
f $ x = f x
и заключая в скобки правую часть выражения, содержащего инфиксный оператор, преобразует его в префиксную функцию, можно написать ($ 3) (4+)
, аналогичную (++", world") "hello"
.
Зачем кому-то это делать? Например, для списков функций. Оба:
map (++", world") ["hello","goodbye"]`
и
map ($ 3) [(4+),(3*)]
короче map (\x -> x ++ ", world") ...
или map (\f -> f 3) ...
. Очевидно, что последние варианты были бы более удобочитаемыми для большинства людей.
Ответ 7
... или вы можете избежать конструкций .
и $
с помощью конвейерной обработки:
third xs = xs |> tail |> tail |> head
Это после добавления в вспомогательную функцию:
(|>) x y = y x
Ответ 8
Мое правило прост (я тоже начинаю):
- не используйте
.
, если вы хотите передать параметр (вызвать функцию) и
- не используйте
$
, если еще нет параметра (составьте функцию)
Это
show $ head [1, 2]
но никогда:
show . head [1, 2]
Ответ 9
Отличный способ узнать больше о чем угодно (любая функция) - это помнить, что все является функцией! Эта общая мантра помогает, но в конкретных случаях, таких как операторы, это помогает запомнить этот маленький трюк:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
и
:t ($)
($) :: (a -> b) -> a -> b
Просто не забудьте использовать :t
либерально и заверните своих операторов в ()
!
Ответ 10
Haskell: разница между .
(точка) и $
(знак доллара)
В чем разница между точкой (.)
и знаком доллара ($)
? Насколько я понимаю, они оба являются синтаксическим сахаром для того, чтобы не использовать скобки.
Они не являются синтаксическим сахаром для того, чтобы не использовать скобки - они являются функциями, - вставлены, поэтому мы можем называть их операторами.
Составь, (.)
, и когда его использовать.
(.)
является функцией композитинга. Итак,
result = (f . g) x
аналогично созданию функции, которая передает результат своего аргумента, переданного в g
, в f
.
h = \x -> f (g x)
result = h x
Используйте (.)
, когда у вас нет доступных аргументов для передачи функциям, которые вы хотите составить.
Право ассоциативного применения, ($)
и когда его использовать
($)
- это правосторонняя функция применения с низким приоритетом связывания. Так что он просто вычисляет вещи справа от него в первую очередь. Таким образом,
result = f $ g x
- это то же самое, что процедурно (что важно, поскольку Haskell оценивается лениво, сначала он начнет оценивать 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) [email protected],
-- or @'Data.List.zipWith' ('$') fs [email protected]
{-# INLINE ($) #-}
($) :: (a -> b) -> a -> b
f $ x = f x
Заключение
Используйте композицию, когда вам не нужно сразу оценивать функцию. Возможно, вы хотите передать функцию, полученную в результате композиции, другой функции.
Используйте приложение, когда вы предоставляете все аргументы для полной оценки.
Так что для нашего примера было бы семантически предпочтительнее сделать
f $ g x
когда у нас есть x
(точнее, g
аргументы), и делаем:
f . g
когда мы не делаем.
Ответ 11
Я думаю, что краткий пример того, где вы бы использовали .
, а не $
, помог бы прояснить ситуацию.
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
Обратите внимание, что times6
- это функция, созданная из композиции функций.
Ответ 12
Все остальные ответы довольно хорошие. Но theres важная деталь удобства юзабилити о том, как ghc лечит $, что ghc type checker допускает instatiarion с более высокими рангами/квантованными типами. Если вы посмотрите на тип $ id
например, вы найдете его функцию, аргумент которой является полиморфной функцией. Маленькие вещи вроде этого arent при одинаковой гибкости с эквивалентным оператором расстроен. (Это действительно заставляет задуматься, стоит ли $! Заслуживает того же самого лечения или нет)