Haskell:区别。 (点)和$(美元符号)
点(.)
和美元符号($)
之间有什么区别? 据我了解,它们都是不需要使用括号的语法糖。
$
运算符用于避免括号。 任何出现在它之后的东西都将优先于以前的东西。
例如,假设您有一行代码:
putStrLn (show (1 + 1))
如果你想摆脱这些括号,下面的任何一行也会做同样的事情:
putStrLn (show $ 1 + 1) putStrLn $ show (1 + 1) putStrLn $ show $ 1 + 1
的主要目的.
运算符不是为了避免括号,而是为了链接函数。 它可以让你把任何出现在右边的输出与出现在左边的任何input连接起来。 这通常也会减less括号,但工作方式不同。
回到相同的例子:
putStrLn (show (1 + 1))
-
(1 + 1)
没有input,因此不能用于.
运营商。 -
show
可以接受一个Int
并返回一个String
。 -
putStrLn
可以接受一个String
并返回一个IO ()
。
你可以链show
putStrLn
像这样:
(putStrLn . show) (1 + 1)
如果这是你所喜欢的太多括号,用$
运算符来消除它们:
putStrLn . show $ 1 + 1
它们有不同的types和不同的定义:
infixr 9 . (.) :: (b -> c) -> (a -> b) -> (a -> c) (f . g) x = f (gx) infixr 0 $ ($) :: (a -> b) -> a -> b f $ x = fx
($)
是为了替代正常的函数应用,但以不同的优先级来避免括号。 (.)
是为了组合两个函数一起做出一个新的函数。
在某些情况下,它们是可以互换的,但是这通常是不正确的。 典型的例子是:
f $ g $ h $ x
==>
f . g . h $ x
换句话说,在一连串的$
s中,除了最后一个之外,所有的都可以被replace.
还要注意($)
是专门用于函数types的标识函数 。 身份函数如下所示:
id :: a -> a id x = x
($)
看起来像这样:
($) :: (a -> b) -> (a -> b) ($) = id
请注意,我有意在types签名中添加了额外的括号。
($)
使用通常可以通过添加括号来消除(除非操作符在一个段中使用)。 例如: f $ gx
变成f (gx)
。
(.)
用法往往稍微难以取代; 他们通常需要一个lambda或引入一个明确的函数参数。 例如:
f = g . h
变
fx = (g . h) x
变
fx = g (hx)
希望这可以帮助!
($)
允许将函数链接在一起而不添加括号来控制评估顺序:
Prelude> head (tail "asdf") 's' Prelude> head $ tail "asdf" 's'
组合运算符(.)
创build一个新的函数,但不指定参数:
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 = fx
并将包含中缀运算符的expression式的右侧括起来,将其转换为前缀函数,可以类似于(++", world") "hello"
来写($ 3) (4+)
。
为什么有人会这样做? 例如,对于function列表。 都:
map (++", world") ["hello","goodbye"]`
和:
map ($ 3) [(4+),(3*)]
比map (\x -> x ++ ", world") ...
或map (\f -> f 3) ...
短。 显然,后者的变体对于大多数人来说更易读。
…或者你可以避免的.
和$
使用stream水线结构 :
third xs = xs |> tail |> tail |> head
这是在添加辅助函数之后:
(|>) xy = yx
了解更多关于任何东西(任何function)的好方法是记住一切都是一个function! 这个通用的口头禅可以帮上忙,但是在像操作员这样的特殊情况下,记住这个小窍门会有帮助:
: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
是从函数组合创build的函数。
Haskell:区别
.
(点)和$
(美元符号)点
(.)
和美元符号($)
之间有什么区别? 据我所知,它们都是不需要使用括号的语法糖。
由于不需要使用括号,它们不是句法糖 – 它们是function, – 是固定的,因此我们可以称它们为操作符。
(.)
是组合function。 所以
result = (f . g) x
与构build一个将传递给g
参数的结果传递给f
。
h = \x -> f (gx) result = hx
($)
是具有低绑定优先级的右联合应用函数。 所以它只是首先计算它的权利。 从而,
result = f $ gx
和程序上的一样(这很重要,因为Haskell是懒惰的评估,它会先开始评估f
):
h = f g_x = gx result = h g_x
或者更简洁:
result = f (gx)
我们可以通过阅读每个函数的源代码来看到这一点。
阅读来源
这是(.)
的来源 :
-- | 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 (.) fg = \x -> f (gx)
这是($)
的来源 :
-- | Application operator. This operator is redundant, since ordinary -- application @(fx)@ 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 $ hx = f (g (hx)) -- -- 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 = fx
何时使用:
当你不需要立即评估function时使用组合。 也许你想把构成的function传递给另一个function。
在提供所有参数以进行全面评估时使用应用程序。
所以对于我们的例子来说,这样做在语义上是可取的
f $ gx
当我们有x
(或者说, g
的参数),并且:
f . g
当我们不。