-- | 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
何时使用:
当您不需要立即评估函数时使用合成 . 也许你想将由合成产生的函数传递给另一个函数 .
在提供完整评估的所有参数时使用应用程序 .
因此,对于我们的示例,它在语义上是可取的
f $ g x
当我们有 x (或更确切地说, g 的参数),并且:
f . g
当我们没有 .
0
我想一个简短的例子,你将使用 . 而不是 $ 将有助于澄清事情 .
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
12 回答
$
运算符用于避免括号 . 之后出现的任何内容都将优先于之前的任何内容 .例如,假设您有一行内容如下:
如果你想摆脱这些括号,以下任何一行也会做同样的事情:
.
运算符的主要目的不是避免括号,而是链函数 . 它允许您将右侧显示的内容输出与左侧显示的输入相关联 . 这通常也会导致括号减少,但工作方式不同 .回到相同的例子:
(1 + 1)
没有输入,因此不能与.
运算符一起使用 .show
可以Int
并返回String
.putStrLn
可以String
并返回IO ()
.您可以将
show
链接到putStrLn
,如下所示:如果您喜欢这个括号太多,请使用
$
运算符删除它们:它们有不同的类型和不同的定义:
($)
旨在取代正常的功能应用程序,但具有不同的优先级以帮助避免使用括号 .(.)
用于组合两个函数以创建新函数 .在某些情况下,它们是可以互换的,但总的来说并非如此 . 它们的典型示例是:
==>
换句话说,在
$
的链中,除最后一个之外的所有部分都可以被.
取代另请注意,
($)
是专用于函数类型的标识函数 . 标识函数如下所示:虽然
($)
看起来像这样:请注意,我有意在类型签名中添加了额外的括号 .
通常可以通过添加括号来消除
($)
的使用(除非在一个部分中使用运算符) . 例如:f $ g x
变为f (g x)
.(.)
的使用通常稍微难以替换;它们通常需要lambda或引入显式函数参数 . 例如:变
变
希望这可以帮助!
($)
允许将函数链接在一起而不添加括号来控制评估顺序:撰写运算符
(.)
创建一个新函数而不指定参数:上面的例子可以说是说明性的,但并没有真正显示使用组合的便利性 . 这是另一个类比:
如果我们只使用第三次,我们可以避免使用lambda来命名它:
最后,组合让我们避免lambda:
简短而甜蜜的版本:
($)
调用函数,该函数是其右手参数值的左侧参数 .(.)
组成函数,它是函数的左手参数,它是右手参数 .一个有用的应用程序,花了我一些时间从非常简短的描述中找出at learn you a haskell:从:
并且将包含中缀运算符的表达式的右侧括起来将其转换为前缀函数,可以将
($ 3) (4+)
类似于(++", world") "hello"
.为什么有人会这样做?例如,对于函数列表 . 都:
和:
比
map (\x -> x ++ ", world") ...
或map (\f -> f 3) ...
短 . 显然,后者的变体对大多数人来说更具可读性 ....或者您可以通过使用流水线来避免
.
和$
构造:在您添加辅助函数之后:
了解任何事物(任何功能)的好方法是记住一切都是功能!一般的口头禅有所帮助,但在特定情况下,如操作员,它有助于记住这个小技巧:
和
请记住自由地使用
:t
,并将您的运算符包装在()
中!我的规则很简单(我也是初学者):
如果要传递参数(调用函数),请不要使用
.
如果还没有参数,则不要使用
$
(撰写函数)那是
但从不:
它们是 not 语法糖,不需要使用括号 - 它们是函数, - 无效,因此我们可以称它们为运算符 .
(.)
是撰写函数 . 所以与构建一个函数相同,该函数将传递给
g
的参数的结果传递给f
.($)
是具有低绑定优先级的右关联应用函数 . 所以它只是首先计算它右边的东西 . 从而,在程序上是这样的(这很重要,因为Haskell被懒惰地评估,它将首先开始评估
f
):或者更简洁:
我们可以通过阅读每个函数的源来看到这一点 .
阅读来源
这是source for
(.)
:这是source为
($)
:何时使用:
当您不需要立即评估函数时使用合成 . 也许你想将由合成产生的函数传递给另一个函数 .
在提供完整评估的所有参数时使用应用程序 .
因此,对于我们的示例,它在语义上是可取的
当我们有
x
(或更确切地说,g
的参数),并且:当我们没有 .
我想一个简短的例子,你将使用
.
而不是$
将有助于澄清事情 .请注意,
times6
是根据函数组合创建的函数 .所有其他答案都很好 . 但是关于ghc如何处理$,有一个重要的可用性细节,ghc类型检查器允许更高级别/量化类型的instatiarion . 如果你看一下
$ id
的类型,你会发现它将采用一个函数,其参数本身就是一个多态函数 . 像这样的小事情与同等的沮丧操作员没有相同的灵活性 . (这实际上让我想知道$!是否值得同样的待遇)