Haskell中的$是什么,以及如何将函数应用于函数

时间:2018-11-02 15:34:31

标签: function haskell operator-precedence

我正在与Haskell合作进行Euler项目,并在完成the very first problem后发现了一些需要学习的东西。这是我的代码:

isValid x = (mod x 3 == 0) || (mod x 5 == 0)
listValid :: Integer -> [Integer]
listValid n = [x | x <- [1..n-1], isValid x]

函数listValid将获得小于n且可除以3或5的所有正整数。

*Main> listValid 10
[3,5,6,9]

现在我需要总结一下。我认为sum函数是执行此操作的正确方法。我不明白的是为什么前两个版本有效,而第三个版本无效。

*Main> sum (listValid 10)
23
*Main> sum $ listValid 10
23
*Main> sum listValid 10

<interactive>:4:5:
    Couldn't match type ‘[Integer]’ with ‘a0 -> t’
    Expected type: Integer -> a0 -> t
      Actual type: Integer -> [Integer]
    Relevant bindings include it :: t (bound at <interactive>:4:1)
    In the first argument of ‘sum’, namely ‘listValid’
    In the expression: sum listValid 10

这是一个操作顺序问题吗,我需要在括号中括起来以断言应该首先应用哪个函数吗?如果是这样,$在第二个版本中做什么?

4 个答案:

答案 0 :(得分:5)

这是关于关联性。函数应用是左关联的,因此sum listValid 10等效于(sum listValid) 10,而不是sum (listValid 10)。而且,如果考虑到这一点,就必须这样:如果定义add x y = x+y,就不会希望add 1 2等同于add (1 2)

所以这里的问题是在sum listValid 10中,它没有将listValid 10视为sum的参数;它会将listValid视为sum的参数,然后将10作为sum listValid的参数。

$解决了此问题,因为它是一个中缀运算符,并且非常清楚地知道sum是它的左操作数,而listValid 10是它的右操作数(请记住,函数应用程序具有更高的优先级而不是任何infix运算符,因此不能将其视为(sum $ listValid) 10

答案 1 :(得分:3)

函数应用程序f x是优先级最高的操作(并且是左关联的),因此

sum listValid 10

等效于(sum listValid) 10

另一方面,$运算符的优先级最低(并且是右关联的,尽管此处无关紧要),所以

sum $ listValid 10

sum $ (listValid 10)隐式相同,而不与(sum $ listValid) 10相同。因此,通常用于消除括号。

答案 2 :(得分:2)

当您编写f $ x时,实际上您是在($) f x的情况下使用($) :: (a -> b) -> a -> b编写一个函数。此函数为defined as

($) :: forall r a (b :: TYPE r). (a -> b) -> a -> b
f $ x = f x

上面看起来并不令人印象深刻。如果您这样写f $ x,就相当于f x,那么为什么还要写$呢?因为此运算符具有precedence 0。因此,这意味着如果您编写:

f $ x+2

它解释为:

($) f (x+2)

因此:

f (x+2)

无需括号。

回到您的问题,如果您写:

sum $ listValid 10

这被解析为:

($) (sum) (listValid 10)

,因此在功能上等同于:

sum (listValid 10)

但是,如果您写:

sum listValid 10

Haskell将此解释为:

(sum listValid) 10

现在,类型为sum的函数的Integer -> [Integer]没有意义,sum :: Num a => [a] -> a应该采用Num个数值的列表,因此会出现错误。

答案 3 :(得分:2)

函数应用是左关联的,所以

f x y

解析为:

(f x) y

但是,函数应用程序的优先级高于任何中缀运算符,因此

f x $ g y

解析为:

(f x) $ (g y)

特别是,您具有:

sum listValid 10 = (sum listValid) 10
sum $ listValid 10 = sum $ (listValid 10) = sum (listValid 10)