Haskell中的$是什么,如何对函数应用函数
What is $ in Haskell, and how to apply functions to functions
我正在用 Haskell 做 Project 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
这是操作顺序问题吗,我需要用括号括起来以断言应该首先应用哪个函数?如果是这样,$
在第二个版本中做什么?
函数应用f x
是最高优先级的操作(和左关联),因此
sum listValid 10
等同于(sum listValid) 10
.
另一方面,$
运算符具有最低的优先级(并且是右结合的,尽管此处不相关),因此
sum $ listValid 10
隐含地与 sum $ (listValid 10)
相同,而不是 (sum $ listValid) 10
。因此,它通常用于消除括号。
这是关于关联性的。函数应用是左结合的,所以 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
是它的右操作数(记住函数应用程序有优先级高于任何中缀运算符,因此它不能被视为 (sum $ listValid) 10
).
当你写f $ x
时,你实际上写了($) f x
,($) :: (a -> b) -> a -> b
a function. This function is 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
现在 Integer -> [Integer]
类型的函数的 sum
没有意义,sum :: Num a => [a] -> a
应该采用 Num
erical 值的列表,因此出现错误。
函数应用是左结合的,所以
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)
我正在用 Haskell 做 Project 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
这是操作顺序问题吗,我需要用括号括起来以断言应该首先应用哪个函数?如果是这样,$
在第二个版本中做什么?
函数应用f x
是最高优先级的操作(和左关联),因此
sum listValid 10
等同于(sum listValid) 10
.
另一方面,$
运算符具有最低的优先级(并且是右结合的,尽管此处不相关),因此
sum $ listValid 10
隐含地与 sum $ (listValid 10)
相同,而不是 (sum $ listValid) 10
。因此,它通常用于消除括号。
这是关于关联性的。函数应用是左结合的,所以 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
是它的右操作数(记住函数应用程序有优先级高于任何中缀运算符,因此它不能被视为 (sum $ listValid) 10
).
当你写f $ x
时,你实际上写了($) f x
,($) :: (a -> b) -> a -> b
a function. This function is 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
现在 Integer -> [Integer]
类型的函数的 sum
没有意义,sum :: Num a => [a] -> a
应该采用 Num
erical 值的列表,因此出现错误。
函数应用是左结合的,所以
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)