В чем разница между точкой (.)
и знаком доллара ($)
?
Насколько я понимаю, они оба являются синтаксическим сахаром для того, чтобы не использовать скобки.
В чем разница между точкой (.)
и знаком доллара ($)
?
Насколько я понимаю, они оба являются синтаксическим сахаром для того, чтобы не использовать скобки.
Оператор $
предназначен для исключения скобок. Все, что появляется после него, будет иметь приоритет над всем, что было раньше.
Например, скажем, у вас есть строка, которая гласит:
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
У них разные типы и разные определения:
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 все, кроме окончательного, можно заменить на .
Также обратите внимание, что ($)
- это функция идентификации, специализированная для типов функций. Функция идентификации выглядит следующим образом:
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: Поскольку:
f $ x = f x
и заключая в скобки правую часть выражения, содержащего инфиксный оператор, преобразует его в префиксную функцию, можно написать ($ 3) (4+)
, аналогичную (++", world") "hello"
.
Зачем кому-то это делать? Например, для списков функций. Оба:
map (++", world") ["hello","goodbye"]`
и
map ($ 3) [(4+),(3*)]
короче map (\x -> x ++ ", world") ...
или map (\f -> f 3) ...
. Очевидно, что последние варианты были бы более удобочитаемыми для большинства людей.
... или вы можете избежать конструкций .
и $
с помощью конвейерной обработки:
third xs = xs |> tail |> tail |> head
Это после добавления в вспомогательную функцию:
(|>) x y = y x
Мое правило прост (я тоже начинаю):
.
, если вы хотите передать параметр (вызвать функцию) и$
, если еще нет параметра (составьте функцию)Это
show $ head [1, 2]
но никогда:
show . head [1, 2]
Отличный способ узнать больше о чем угодно (любая функция) - это помнить, что все является функцией! Эта общая мантра помогает, но в конкретных случаях, таких как операторы, это помогает запомнить этот маленький трюк:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
и
:t ($)
($) :: (a -> b) -> a -> b
Просто не забудьте использовать :t
либерально и заверните своих операторов в ()
!
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
когда мы не делаем.
Я думаю, что краткий пример того, где вы бы использовали .
, а не $
, помог бы прояснить ситуацию.
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
Обратите внимание, что times6
- это функция, созданная из композиции функций.
Все остальные ответы довольно хорошие. Но theres важная деталь удобства юзабилити о том, как ghc лечит $, что ghc type checker допускает instatiarion с более высокими рангами/квантованными типами. Если вы посмотрите на тип $ id
например, вы найдете его функцию, аргумент которой является полиморфной функцией. Маленькие вещи вроде этого arent при одинаковой гибкости с эквивалентным оператором расстроен. (Это действительно заставляет задуматься, стоит ли $! Заслуживает того же самого лечения или нет)