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 + 1)没有input,因此不能用于. 运营商。
  2. show可以接受一个Int并返回一个String
  3. 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 

当我们不。