Answers:
该$
操作是为了避免括号。之后出现的所有内容将优先于之前出现的所有内容。
例如,假设您有一行显示:
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
putStrLn . show . (+1) $ 1
将是等效的。您是正确的,因为大多数(全部?)中缀运算符都是函数。
map ($3)
。我的意思是,我通常$
也尽量避免使用括号,但这并不是他们所需要的全部。
map ($3)
是type的函数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 链中,除最后一个外的所有s都可以替换为.
x
是一个函数怎么办?然后可以.
用作最后一个吗?
x
在这种情况下申请,那么可以-但是“最终”申请将适用于而不是x
。如果您不申请x
,那么x
与值一样。
另请注意,这($)
是专用于功能类型的身份功能。身份函数如下所示:
id :: a -> a
id x = x
虽然($)
看起来像这样:
($) :: (a -> b) -> (a -> b)
($) = id
请注意,我有意在类型签名中添加了额外的括号。
的用途($)
,通常可以通过添加括号(除非操作者在一个部分中使用)来消除。例如:f $ g x
变成f (g x)
。
的使用(.)
通常较难替换;他们通常需要lambda或引入显式函数参数。例如:
f = g . h
变成
f x = (g . h) x
变成
f x = g (h x)
希望这可以帮助!
($)
允许将函数链接在一起,而无需添加括号来控制评估顺序:
Prelude> head (tail "asdf")
's'
Prelude> head $ tail "asdf"
's'
compose运算符(.)
无需指定参数即可创建一个新函数:
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"
如果只使用一次三次,则可以避免使用lambda命名:
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
最后,组合使我们避免使用lambda:
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
一个有用的应用程序让我花了一些时间从简短的说明中了解到您学习haskell:
f $ x = f x
并用括号括起包含infix运算符的表达式的右侧,可将其转换为前缀函数,可以($ 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 :(
.
点)和$
(美元符号)之间的区别点
(.)
和美元符号有($)
什么区别?据我了解,它们都是不需要使用括号的语法糖。
他们是不是不需要使用括号语法糖-他们是功能- infixed,因此,我们可以称他们为运营商。
(.)
以及何时使用它。(.)
是撰写功能。所以
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) xs@,
-- or @'Data.List.zipWith' ('$') fs xs@.
{-# INLINE ($) #-}
($) :: (a -> b) -> a -> b
f $ x = f x
当您不需要立即评估该功能时,请使用组合。也许您想将合成产生的功能传递给另一个功能。
提供所有参数以进行全面评估时,请使用application。
因此,对于我们的示例,在语义上更可取
f $ g x
当我们有x
(或者更确切地说g
是的参数)并执行以下操作时:
f . g
当我们不这样做时。
...或者您可以通过使用流水线来避免.
和$
结构:
third xs = xs |> tail |> tail |> head
那是在您添加了辅助函数之后:
(|>) x y = y x
$
运算符实际上<|
比F#更像F#|>
,通常在haskell中,您应该像这样编写上述函数:third xs = head $ tail $ tail $ xs
甚至可能像third = head . tail . tail
,在F#风格的语法中,它应该是这样的:let third = List.head << List.tail << List.tail
我的规则很简单(我也是初学者):
.
如果要传递参数(调用函数),请不要使用,并且$
如果没有参数,请不要使用(组成一个函数)那是
show $ head [1, 2]
但永远不会:
show . head [1, 2]
所有其他答案都很好。但是,有一个重要的可用性细节涉及ghc如何处理$,ghc类型检查器允许具有较高等级/量化类型的状态。如果您查看$ id
例如的类型, 您会发现它将使用一个函数,其参数本身就是一个多态函数。像这样的小事情在使用等效的翻转运算符时不会具有相同的灵活性。(这实际上使我怀疑$!是否应得到相同的待遇)