两个函数之间的 space 和使用组合运算符有什么区别?

What is the difference between a space between two functions and using the composition operator?

我试图定义一个函数来确定一个数字是否是 3 的倍数,我首先做了简单的案例

isMultipleOfThree num = (==0) (rem num 3)

但实际上,即使在那种简单的情况下,我的第一个猜测是像这样写我的解决方案

isMultipleOfThree num = (==0).(rem num 3)

哪个returns错误

Non type-variable argument in the constraint: Integral (a -> b) (Use FlexibleContexts to permit this) When checking that ‘isMultipleOfThree’ has the inferred type isMultipleOfThree :: forall b a. (Eq b, Integral (a -> b), Num b) => (a -> b) -> a -> Bool

然后尝试让它更“多态”:

正确答案如下,但我认为我应该使用 space 而不是组合运算符......然后我只是“猜测”我会尝试使用组合运算符:

esMultiploDeTres = (==0).(flip (rem) 3)

所以我想我会添加到我原来的问题:“两个函数之间的 space 是什么?”

isMultipleOfThree num = (==0) (rem num 3)

(== 0) 称为运算符部分。它等同于 \x -> x == 0,因此与

相同
isMultipleOfThree num = (\x -> x == 0) (rem num 3)

我们立即应用 lambda,所以我们得到

isMultipleOfThree num = rem num 3 == 0

现在,另一方面,你的第二个例子是

isMultipleOfThree num = (\x -> x == 0) . (rem num 3)

现在我们不是立即应用 lambda。我们正在应用 (.) 函数,其定义类似于

(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \y -> f (g y)

因此,我们有

isMultipleOfThree num = (\y -> (\x -> x == 0) ((rem num 3) y))

现在 x 上的 lambda 正被应用于一个参数,所以我们可以简化内部的

isMultipleOfThree num = (\y -> (rem num 3) y == 0)

并且 rem 周围的括号是无关紧要的,因此

isMultipleOfThree num = (\y -> rem num 3 y == 0)

因此,我们将 rem 一个具有两个参数的函数应用于三个参数。因此你的错误。

如果您想使用 (.) 来定义您的函数,我们可以。但是右边需要省略一个参数。

isMultipleOfThree = (== 0) . (\x -> rem x 3)

isMultipleOfThree = (== 0) . (`rem` 3)

请注意,我们不再提及您的函数参数 num。右侧的构图正在等待争论。它将执行 (rem 3)on that argument and then compare the result against zero.(.)works on functions, and both sides of the(.)` operator should have an argument missing.

注意你可能想到的是($),大致定义为

($) :: (a -> b) -> (a -> b)
f $ x = f x

这真的只是功能应用。这不是作曲。我们可以轻松地将您的原始函数写成

isMultipleOfThree num = (== 0) $ (rem num 3)

($) 实际上确实表现得像并列。唯一的一点是改变运算符的优先级。在这种情况下我们可以省略括号

isMultipleOfThree num = (== 0) $ rem num 3

这仍然有效,而如果我们在您的原始函数中省略括号

isMultipleOfThree num = (==0) rem num 3 -- Wrong!

现在我们试图将 (==0) 函数应用于 三个 个参数,这绝对是不正确的。

(.) 简单定义为

# Apply g to x, then apply f to the result.
f . g = \x -> f (g x)

这意味着

(f . g) x == f (g x)

并列表示功能应用,而.表示功能组合.

高阶函数,例如(.),可以将其他函数作为参数,这意味着您可以将这样的函数应用于另一个函数。 f . g 是一个中缀表达式,相当于前缀表达式 (.) f g,其中 (.) 应用于 f,结果应用于 g