howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c
| a && b && c >= m = 3
| a && b >= m = 2
| b && c >= m = 2
| a && c >= m = 2
| a || b || c >= m = 1
| otherwise = 0
where m = a + b + c
请问为什么这段代码没有运行,我收到很多我不明白的错误
你在代码中写道:
howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c
| a && b && c >= m = 3
| a && b >= m = 2
| b && c >= m = 2
| a && c >= m = 2
| a || b || c >= m = 1
| otherwise = 0
where m = a + b + c
我在这里用粗体显示一个案例,但错误适用于所有案例。你写作为条件a && b && c >= m
.对于Haskell来说,这意味着你像a && b && (c >= m)
一样写它。现在(&&)
是一个运算符,它将两个布尔值作为操作数,并计算这两个操作数的逻辑和。但操作数不是布尔值。c >= m
是布尔值,但a
和b
不是布尔值。
所以我们必须在所有操作数上写条件,例如:
howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c
| a>= m&& b>= m&& c >= m = 3
| a>= m&& b >= m = 2
| b>= m&& c >= m = 2
| a>= m&& c >= m = 2
| a>= m|| b>= m|| c >= m = 1
| otherwise = 0
where m = a + b + c
所以现在我们有布尔值,程序将编译,但它在语义上仍然是不连贯的。平均值是元素的总和除以元素的数量。这里的元素数是 3,所以我们应该把它除以 3。由于除以一个数字会导致数值错误,并且意味着我们必须在浮点世界中工作,因此将我们与之比较的元素乘以 3 更安全,因此:
howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c
|a3>= m &&b3>= m &&c3>= m = 3
|a3>= m &&b3>= m = 2
|b3>= m &&c3>= m = 2
|a3>= m &&c3>= m = 2
|a3>= m ||b3>= m ||c3>= m = 1
| otherwise = 0
where m = a + b + c
a3 = 3 * a
b3 = 3 * b
c3 = 3 * c
现在它可以工作,但仍然不优雅:它需要五个警卫来检查条件的数量,总共十二个比较。因此,这将相当低效。
更好的主意可能是将True
转换为1
,False
转换为0
,我们可以使用bool
同胚函数来做到这一点:bool :: a -> a -> Bool -> a
。如果我们像bool 0 1
一样构造它,那么我们有一个函数,可以将False
转换为0
,True
转换为1
,然后我们可以这样写:
import Data.Bool(bool)
howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c =booltoint (3*a >= m)
+ booltoint (3*b >= m)
+ booltoint (3*c >= m)
where m = a + b + c
booltoint = bool 0 1
它可以进一步改进,但我把它留作一个练习。
&&
运算符的类型为Bool -> Bool -> Bool
。这意味着必须Bool
两个操作数,结果也是一个Bool
。
例如,在表达式a && b >= m
中,右操作数(b >= m
)是Bool
,但左操作数(a
)不是-它是一个Int
。
所以这就是为什么你得到一个错误:左操作数也需要是一个Bool
。