我正在与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
这是一个操作顺序问题吗,我需要在括号中括起来以断言应该首先应用哪个函数吗?如果是这样,$
在第二个版本中做什么?
答案 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)