点号(.)和美元符号($)有什么区别?据我所知,它们都是句法上的糖分,不需要使用括号。
$运算符用于避免括号。它之后出现的任何东西将优先于之前出现的任何东西。
例如,假设您有一行代码:
如果您想去掉这些括号,下面的任何一行也会做同样的事情:
.运算符的主要目的不是避免括号,而是链函数。它允许您将右侧显示的任何内容的输出与左侧显示的任何内容的输入绑定起来。这通常也会导致括号更少,但工作方式不同。
回到同样的例子:
(1 + 1)没有输入,因此不能与.运算符一起使用。
show可以取一个Int返回一个String。
putStrLn可以取一个String返回一个IO ()。
您可以这样将show链接到putStrLn:
如果括号太多了,请使用$运算符将其去掉:
- 实际上,既然+也是一个函数,你不能先加前缀然后再组合它,比如'putstrln'。表演。(+)11`不是说更清楚了,但我的意思是…你可以,对吧?
- @在本例中,类似于putStrLn . show . (+1) $ 1的代码xarcanum是等效的。你说得最对吗?中缀运算符是函数。
- 我真的认为,在看到putStrLn $ show $ 1 + 1之后,$必须换成一个轻量级的角色。我现在想不出任何一个,因为它们都用光了,但会有一些人物出现。
- @codexarcanum:putStrLn . show . (+) 1 1不起作用。
- @纳瓦兹说得对,你得做点什么(putsrln。表演。(+ 1)1
- 我想知道为什么没有人提到像map ($3)这样的用法。我的意思是,我通常也用$来避免括号,但它们不只是为了这个。
- 不过,使用美元时要非常小心。它倾向于将括号继续到行的末尾,或者括号的末尾在里面。在我学习这门语言的时候,给我带来了许多奇怪的结果。
- @Cubic那个地图声明应该做什么?
- map ($3)是Num a => [(a->b)] -> [b]型函数。它获取一个函数列表,获取一个数字,将3应用于所有函数,并收集结果。
- @Cubic,非常感谢你,起初我没有摸索你的意思,但是ghci的类型系统帮助了我!对于您的启发,我的例子;---代码--让我们试试::[整数->B]->[B];让我们试试=映射($3);让我们试试adda::integer->integer;让我们试试x=7+x;让我们试试suba::integer->integer;suba x=x-7;--现在开始做:--;让我们试试[adda,suba];结果=[10,-4]/////很抱歉,我们不能将注释格式化为代码
- 在与其他操作员一起使用$时必须小心。"x+f(y+z)"与"x+f$y+z"不同,因为后者实际上意味着"(x+f)(y+z)"(即x和f之和被视为一个函数)。
- @注释性文件:如果你想要一个简明的例子:map ($3) [(\x->x+7),(\x->x-7)]。我倾向于将$理解为在我的头脑中适用于,所以您可以在使用map的列表中应用3到2个数字转换函数。
- @不,你不能。(+)是一个"2参数函数",因此您的示例不起作用。但是,您可以使用函数组合的组合(所谓的"tits operator"):putStrLn ((.).(.)) show ((.).(.)) (+) $ 1 1。我通常定义(.:) = (.).(.),然后(putStrLn . show) .: (+) $ 1 1就可以了。)
它们有不同的类型和定义:
1 2 3 4 5 6 7
| infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x |
($)用于替换普通函数应用程序,但优先级不同,有助于避免括号。(.)是将两个函数组合在一起形成一个新的函数。
在某些情况下,它们是可互换的,但一般来说这是不正确的。它们的典型示例是:
=>
换言之,在$的链中,除最后一个外,其余的都可以用.代替。
- 如果x是一个函数呢?那么你能用.作为最后一个吗?
- @Richizy如果你真的在这个上下文中应用了x,那么是的-但是最后一个应用将应用于除x之外的东西。如果您不应用x,那么它与作为值的x没有区别。
还要注意,($)是专门针对函数类型的标识函数。Identity函数如下所示:
虽然($)看起来是这样的:
1 2
| ($) :: (a -> b ) -> (a -> b )
($) = id |
注意,我有意在类型签名中添加了额外的括号。
使用($)通常可以通过添加括号来消除(除非在节中使用了运算符)。例如:f $ g x变为f (g x)。
使用(.)通常比较难替换;它们通常需要lambda或引入显式函数参数。例如:
变成
变成
希望这有帮助!
- "注意,我有意在类型签名中添加了额外的括号。"我很困惑…你为什么这么做?
- @Mateenulhaq($)的类型是(A->B->A->B,这与(A->B->(A->B)相同,但是这里的附加括号增加了一些清晰度。
- 哦,我想。我认为这是两个论点的函数…但由于货币化,它完全等同于返回函数的函数。
($)允许将函数链接在一起,而无需为控制评估顺序添加括号:
compose运算符(.)在不指定参数的情况下创建一个新函数:
1 2 3 4 5 6 7
| Prelude > let second x = head $ tail x
Prelude > second "asdf"
's'
Prelude > let second = head . tail
Prelude > second "asdf"
's' |
上面的示例可以说是说明性的,但并没有真正显示使用合成的便利性。下面是另一个类比:
1 2 3
| Prelude > let third x = head $ tail $ tail x
Prelude > map third ["asdf", "qwer", "1234"]
"de3" |
如果我们只使用第三次,我们可以避免使用lambda来命名它:
最后,合成让我们避免lambda:
- 如果stackoverflow有一个组合函数,我更喜欢将前面的两个解释与这个答案中的示例结合起来。
简短而甜蜜的版本:
- ($)调用函数,该函数是它在值上的左侧参数,而该值是它的右侧参数。
- (.)组成的函数是它的左参数,而函数是它的右参数。
有一个应用程序很有用,我花了一些时间从"学习哈斯克尔"的简短描述中找到答案:因为:
在包含中缀运算符的表达式的右侧加括号,将其转换为前缀函数,可以编写类似于(++", world")"hello"的($ 3) (4+)。
为什么会有人这么做?例如,函数列表。两者:
1
| map (++", world") ["hello", "goodbye"]` |
还有:
比map (\x -> x ++", world") ...或map (\f -> f 3) ...短。显然,后一种变体对大多数人来说更易于阅读。
- 顺便说一句,我建议不要在没有空间的情况下使用$3。如果启用模板haskell,这将被解析为一个拼接,而$ 3始终意味着您所说的。一般来说,haskell似乎有一种趋势,即通过坚持某些运算符在其周围有空格来"窃取"语法位。
- 我花了一段时间才弄清楚括号是如何工作的:en.wikibooks.org/wiki/haskell/…
…或者您可以通过使用管道来避免.和$结构:
在添加了helper函数之后:
- 是的,>是F管道运营商。
- 这里要注意的一点是,haskell的$操作符实际上比它的|>更像f的<|,通常在haskell中,您会这样编写上面的函数:third xs = head $ tail $ tail $ xs或者甚至可能像third = head . tail . tail,在f样式语法中,它会像这样:let third = List.head << List.tail << List.tail。
- 为什么要添加一个助手函数,使haskell看起来像f_?- 1
- 翻转的$已经有了,叫做&hackage.haskell.org/package/base-4.8.0.0/docs/&hellip;
了解任何事物(任何函数)的一个好方法是记住,所有事物都是一个函数!一般的咒语是有帮助的,但在某些特殊情况下,如操作员,记住这一小诀窍是有帮助的:
1 2
| :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c |
和
1 2
| :t ($)
($) :: (a -> b) -> a -> b |
记住要充分使用:t,并用()包装您的操作员!
我的规则很简单(我也是初学者):
- 如果要传递参数(调用函数),不要使用.,并且
- 如果还没有参数(组成函数),则不要使用$。
那就是
但绝不会:
Haskell: difference between . (dot) and $ (dollar sign)
What is the difference between the dot (.) and the dollar sign ($)?. As I understand it, they are both syntactic sugar for not needing to use parentheses.
它们不是不需要使用括号的句法糖分-它们是函数,-中缀,因此我们可以称它们为运算符。
撰写,
(.),以及何时使用。
(.)是复合函数。所以
与构建一个函数相同,该函数将传递给g的参数结果传递给f。
1 2
| h = \x -> f (g x)
result = h x |
当您没有可用于传递给您希望编写的函数的参数时,使用(.)。
右联想应用,
($),何时使用
($)是一个右联合应用函数,具有低绑定优先级。所以它只是先计算它右边的东西。因此,
与此相同,程序上(由于Haskell的评估不及时,因此它将首先开始评估f):
1 2 3
| h = f
g_x = g x
result = h g_x |
或者更简明扼要地说:
在将前面的函数应用于结果之前,当您有所有要评估的变量时,使用($)。
我们可以通过读取每个函数的源代码来看到这一点。
读源
这是(.)的来源:
1 2 3 4 5 6
| -- | 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) |
这是($)的来源:
1 2 3 4 5 6 7 8 9 10 11 12
| -- | 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 |
结论
当您不需要立即评估函数时,可以使用composition。也许你想把合成的函数传递给另一个函数。
在为完整评估提供所有参数时,请使用应用程序。
所以对于我们的例子来说,在语义上最好这样做
当我们有x的论点(或者更确切地说,是g的论点)时,我们应该:
当我们不知道的时候。
其他的答案都很好。但是有一个重要的可用性细节是关于ghc如何处理$的,ghc类型检查器允许Instatiarion具有更高级别/量化的类型。例如,如果你看$ id的类型,你会发现它将采用一个自变量为多态函数的函数。像这样的小事情,对于一个等价的烦扰操作符,并没有同样的灵活性。(这真的让我怀疑是不是美元!是否需要同样的待遇)
我认为一个简短的例子,说明你在哪里使用.而不是$,将有助于澄清问题。
1 2 3 4 5 6
| double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c |
注意,times6是一个由函数组合创建的函数。