有什么区别.(点)和$(美元符号)?

Rab*_*ski 682 syntax haskell function-composition

(.)和美元符号有($)什么区别?据我了解,它们都是不需要使用括号的语法糖.

Mic*_*ele 1190

$操作是为了避免括号.在它之后出现的任何东西都将优先于之前的任何东西.

例如,假设您有一行内容如下:

putStrLn (show (1 + 1))
Run Code Online (Sandbox Code Playgroud)

如果你想摆脱这些括号,以下任何一行也会做同样的事情:

putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
Run Code Online (Sandbox Code Playgroud)

.运算符的主要目的不是避免括号,而是链函数.它允许您将右侧显示的内容的输出与左侧显示的输入相关联.这通常也会导致括号减少,但工作方式也不同.

回到同一个例子:

putStrLn (show (1 + 1))
Run Code Online (Sandbox Code Playgroud)
  1. (1 + 1)没有输入,因此不能与.操作员一起使用.
  2. show可以拿一个Int并返回一个String.
  3. putStrLn可以拿一个String并返回一个IO ().

你可以链接showputStrLn这样:

(putStrLn . show) (1 + 1)
Run Code Online (Sandbox Code Playgroud)

如果您喜欢这个括号太多,请与$运营商一起摆脱它们:

putStrLn . show $ 1 + 1
Run Code Online (Sandbox Code Playgroud)

  • 我想知道为什么没有人提到像`map($ 3)`这样的用法.我的意思是,我大多使用`$`来避免使用括号,但它并不是那样的. (77认同)
  • 实际上,由于+也是一个函数,你不能把它作为前缀,然后组合它,就像`putStrLn.节目 .(+)1 1`不是说它更清楚,但我的意思是......你可以,对吧? (47认同)
  • `map($ 3)`是`Num a => [(a-> b)] - > [b]`类型的函数.它需要一个带有数字的函数列表,对所有函数应用3并收集结果. (42认同)
  • 与其他运算符一起使用$时必须小心."x + f(y + z)"与"x + f $ y + z"不同,因为后者实际上意味着"(x + f)(y + z)"(即x和f的总和是作为一种功能处理). (18认同)
  • @CodexArcanum在这个例子中,类似于`putStrLn.节目 .(+ 1)$ 1`将是等价的.你是正确的,因为大多数(所有?)中缀运算符都是函数. (4认同)
  • 但是要小心使用$.它倾向于将括号继续到行的末尾,或者它在里面的括号的末尾.当我学习这门语言时,给我带来了许多奇怪的结果. (3认同)
  • @CodexArcanum 不,你不能。`(+)` 是一个“2 个参数函数”,所以你的例子不起作用。但是,您可以使用函数组合的组合(所谓的“tits 运算符”)来完成此操作:`putStrLn ((.).(.)) show ((.).(.)) (+) $ 1 1`。我通常定义 `(.:) = (.).(.)` 然后 `(putStrLn . show) .: (+) $ 1 1` 工作正常:) (3认同)
  • @AnneTheAgile:如果你想要一个简洁的例子:`map ($3) [(\x->x+7),(\x->x-7)]`。我倾向于将“$”读作“适用于”,因此您可以使用 map 将数字 3“应用”到列表中的两个数字转换函数。 (2认同)
  • @WojciechDanilo,啊哈哈哈,_奶子操作员_!它会深深地印在我的脑海里! (2认同)

GS *_*ica 180

它们有不同的类型和不同的定义:

infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)

infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
Run Code Online (Sandbox Code Playgroud)

($)旨在取代正常的功能应用程序,但在不同的优先级,以帮助避免括号.(.)用于组合两个函数以创建新函数.

在某些情况下,它们是可以互换的,但总的来说并非如此.它们的典型示例是:

f $ g $ h $ x
Run Code Online (Sandbox Code Playgroud)

==>

f . g . h $ x
Run Code Online (Sandbox Code Playgroud)

换句话说,在一连串的$s中,除最后一个之外的所有部分都可以被替换.

  • 如果“x”是一个函数怎么办?那么你可以使用“.”作为最后一个吗? (3认同)
  • @richizy如果你实际上在这个上下文中应用`x`,那么是 - 但是那么"最终的"将适用于除了`x`以外的其他东西.如果你没有应用`x`,那么`x`就是一个值也没有什么不同. (3认同)

Mar*_*ijn 122

另请注意,这($)专用于函数类型的标识函数.标识函数如下所示:

id :: a -> a
id x = x
Run Code Online (Sandbox Code Playgroud)

虽然($)看起来是这样的:

($) :: (a -> b) -> (a -> b)
($) = id
Run Code Online (Sandbox Code Playgroud)

请注意,我有意在类型签名中添加了额外的括号.

使用的($),通常可以通过添加括号(除非操作者在一个部分中使用)来消除.例如:f $ g x成为f (g x).

使用(.)通常稍微难以更换; 它们通常需要lambda或引入显式函数参数.例如:

f = g . h
Run Code Online (Sandbox Code Playgroud)

f x = (g . h) x
Run Code Online (Sandbox Code Playgroud)

f x = g (h x)
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!

  • @MateenUlhaq($)的类型是(a - > b) - > a - > b,它与(a - > b) - >(a - > b)相同,但额外的括号在这里添加一些明晰. (3认同)
  • 哦,我想.我认为它是两个参数的函数...但由于currying,它完全等同于返回函数的函数. (2认同)

小智 77

($) 允许将函数链接在一起而不添加括号来控制评估顺序:

Prelude> head (tail "asdf")
's'

Prelude> head $ tail "asdf"
's'
Run Code Online (Sandbox Code Playgroud)

compose运算符(.)在不指定参数的情况下创建新函数:

Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'

Prelude> let second = head . tail
Prelude> second "asdf"
's'
Run Code Online (Sandbox Code Playgroud)

上面的例子可以说是说明性的,但并没有真正显示使用组合的便利性.这是另一个类比:

Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"
Run Code Online (Sandbox Code Playgroud)

如果我们只使用第三次,我们可以避免使用lambda来命名它:

Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
Run Code Online (Sandbox Code Playgroud)

最后,组合让我们避免lambda:

Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
Run Code Online (Sandbox Code Playgroud)

  • 如果stackoverflow具有组合功能,则我希望将上述两个说明与该答案中的示例结合起来的答案。 (2认同)

ell*_*ben 58

简短而甜蜜的版本:

  • ($) 调用函数,该函数是其右手参数值的左手参数.
  • (.) 组成函数,它是函数的左手参数,它是右手参数.


Chr*_*oph 29

一个有用的应用程序,花了我一些时间从非常简短的描述中弄清楚了解一个haskell:自:

f $ x = f x
Run Code Online (Sandbox Code Playgroud)

并且将包含中缀运算符的表达式的右侧括起来将其转换为前缀函数,可以编写($ 3) (4+)类似的函数(++", world") "hello".

为什么有人会这样做?例如,对于函数列表.都:

map (++", world") ["hello","goodbye"]`
Run Code Online (Sandbox Code Playgroud)

和:

map ($ 3) [(4+),(3*)]
Run Code Online (Sandbox Code Playgroud)

比更短的map (\x -> x ++ ", world") ...map (\f -> f 3) ....显然,后者的变体对大多数人来说更具可读性.

  • 顺便说一句,我建议不要使用`$ 3`而不用空格.如果启用了模板Haskell,则会将其解析为拼接,而"$ 3"始终表示您所说的内容.一般来说,Haskell似乎有一种趋势是通过坚持某些运算符周围的空格来对其进行处理来"窃取"一些语法. (14认同)

Aar*_*all 13

Haskell:(.点)和$(美元符号)之间的差异

(.)和美元符号有($)什么区别?据我了解,它们都是不需要使用括号的语法糖.

他们是不是语法糖不需要使用括号-它们是功能- infixed,因此,我们可以称他们为运营商.

撰写,(.)以及何时使用它.

(.)是撰写功能.所以

result = (f . g) x
Run Code Online (Sandbox Code Playgroud)

是相同的构建通过参数传递到的结果的功能gf.

h = \x -> f (g x)
result = h x
Run Code Online (Sandbox Code Playgroud)

使用(.)时,你没有可用的传递给你希望组成函数的参数.

右关联应用,($)以及何时使用它

($)是具有低绑定优先级的右关联应用函数.所以它只是首先计算它右边的东西.从而,

result = f $ g x
Run Code Online (Sandbox Code Playgroud)

在程序上是相同的(这很重要,因为Haskell被懒惰地评估,它将首先开始评估f):

h = f
g_x = g x
result = h g_x
Run Code Online (Sandbox Code Playgroud)

或者更简洁:

result = f (g x)
Run Code Online (Sandbox Code Playgroud)

($)在将前面的函数应用于结果之前,要使用所有要评估的变量时使用.

我们可以通过阅读每个函数的源来看到这一点.

阅读来源

这里的来源(.):

-- | 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)
Run Code Online (Sandbox Code Playgroud)

而这里的来源($):

-- | 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
Run Code Online (Sandbox Code Playgroud)

结论

在不需要立即评估函数时使用合成.也许你想将由合成产生的函数传递给另一个函数.

在提供完整评估的所有参数时使用应用程序.

因此,对于我们的示例,它在语义上更可取

f $ g x
Run Code Online (Sandbox Code Playgroud)

当我们x(或更确切地说,是g'的论点)时,做:

f . g
Run Code Online (Sandbox Code Playgroud)

当我们没有.


use*_*780 12

...或者您可以通过使用流水线来避免.$构造:

third xs = xs |> tail |> tail |> head
Run Code Online (Sandbox Code Playgroud)

在您添加辅助函数之后:

(|>) x y = y x
Run Code Online (Sandbox Code Playgroud)

  • 翻转的`$`已经可用,它被称为`&`https://hackage.haskell.org/package/base-4.8.0.0/docs/Data-Function.html#v:-38- (9认同)
  • 这里要注意的一点是,Haskell的`$`运算符实际上更像F#的`<|`而不是`|>`,通常在haskell中你会像这样编写上面的函数:`third xs = head $ tail $ tail $ xs`或者甚至可能像`third = head.尾巴 .tail`,在F#样式语法中将是这样的:`let third = List.head << List.tail << List.tail` (6认同)
  • 是的,|>是F#管道运算符. (2认同)

hal*_*csy 11

我的规则很简单(我也是初学者):

  • .如果你想传递参数(调用函数),请不要使用,和
  • $如果还没有参数,请不要使用(撰写函数)

那是

show $ head [1, 2]
Run Code Online (Sandbox Code Playgroud)

但从不:

show . head [1, 2]
Run Code Online (Sandbox Code Playgroud)

  • 良好的启发式,但可以使用更多的例子 (2认同)

lol*_*lol 11

了解任何事物(任何功能)的好方法是记住一切都是功能!一般的口头禅有所帮助,但在特定情况下,如操作员,它有助于记住这个小技巧:

:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)

:t ($)
($) :: (a -> b) -> a -> b
Run Code Online (Sandbox Code Playgroud)

只需记住:t自由使用,并将操作员包裹起来()!