伙计们,我刚刚开始学习haskell(和代码),我遇到了一个我无法弄清楚的问题.所以有这个练习,我必须提出二阶方程的解的数量.
valid a b c = if [a,b,c] == [0,0,0] then False
else (if [a,b] == [0,0] then False
else (if a == 0 then False
else True)) --function to make sure it is a 2nd degree eq
nRaizes a b c = if valid a b c == False then "not a valid eq"
else (if (b^2 - 4 * a * c) > 0 then 2
else (if ((b^2 - 4 * a * c) == 0) then 1
else 0))
Run Code Online (Sandbox Code Playgroud)
一切看起来都不错,但是当我尝试在GHCI中加载脚本时,我收到错误消息:
Could not deduce (Num [Char]) arising from the literal ‘2’
from the context (Num a, Ord a)
bound by the inferred type of
nRaizes :: (Num a, Ord a) => a -> a -> a -> [Char]
at ficha1.hs:(18,1)-(21,13)
In the expression: 2
In the expression:
(if (b * b - 4 * a * c) > 0 then
2
else
(if ((b * b - 4 * a * c) == 0) then 1 else 0))
In the expression:
if valid a b c == False then
"not a valid eq"
else
(if (b * b - 4 * a * c) > 0 then
2
else
(if ((b * b - 4 * a * c) == 0) then 1 else 0))
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)
有人可以向我解释这段代码有什么问题吗?我该如何解决?谢谢
正如我已经评论过的那样,在编写任何实际代码之前,您应始终拥有类型签名.在实际实现任何内容之前,首先要明确代码的用途是什么!
所以,valid取三个数字并以某种方式检查它们,产生False或True- 即一个布尔值.因此,有效的签名将是
valid :: Int -> Int -> Int -> Bool
Run Code Online (Sandbox Code Playgroud)
这会将参数限制为机器大小的整数 - 快速但不溢出安全.它也可能是
valid :: Integer -> Integer -> Integer -> Bool
Run Code Online (Sandbox Code Playgroud)
或者,对于浮点实数,
valid :: Double -> Double -> Double -> Bool
Run Code Online (Sandbox Code Playgroud)
实际上,您不需要确定特定类型:它可以是任何数字类型,它只需要支持相等比较."正确"的签名将是
valid :: (Num a, Eq a) => a -> a -> a -> Bool
Run Code Online (Sandbox Code Playgroud)
如果你只是给它没有类型签名的代码,那确实也是GHC的推论:
Prelude> :t valid
valid :: (Eq a, Num a) => a -> a -> a -> Bool
Run Code Online (Sandbox Code Playgroud)
但编译器只能通过自身实现这一点,因为函数valid恰好是类型正确的.如果你犯了一些错误,那么编译器不知道该类型应该是什么,因此可能推断出一些导致隐藏错误消息的无意义类型.(这只是您应该首先编写签名的原因之一.)
这就是发生的事情nraized.这也需要三个数字并给出一个数字.让我们保持简单:
valid :: Double -> Double -> Double -> Int
Run Code Online (Sandbox Code Playgroud)
这肯定是可以的(虽然你当然可以使它更通用).
现在错误消息更加清晰:
<interactive>:16:87:
Couldn't match expected type ‘Int’ with actual type ‘[Char]’
In the expression: "not a valid eq"
In the expression:
if valid a b c == False then
"not a valid eq"
else
(if (b ^ 2 - 4 * a * c) > 0 then
2
else
(if ((b ^ 2 - 4 * a * c) == 0) then 1 else 0))
Run Code Online (Sandbox Code Playgroud)
这告诉你的是"not a valid eq"与类型不兼容Int.实际上很明显,不是吗?一个应该返回的函数0,1或者2不应该返回一个字符串!
如果您确实希望这是一个错误案例,您应该将其标记为:
nRaizes a b c = if valid a b c == False then error "not a valid eq"
...
Run Code Online (Sandbox Code Playgroud)
在这里,字符串不是结果:如果遇到这种情况,程序将被简单地中止并在用户处提示错误消息,而不是试图将其传递给更多功能(这不可能再给出有意义的结果) ,只是更奇怪的错误).
通常避免嵌套if明确提及True和False- 这是不必要的复杂:无论如何比较产生布尔值.valid如果有任何平等,那就假如错; 这可以写
valid a b c = if [a,b,c] == [0,0,0]
|| [a,b] == [0,0]
|| a == 0
then False
else True
Run Code Online (Sandbox Code Playgroud)
......但那就是一样的
valid a b c = not ([a,b,c] == [0,0,0] || [a,b] == [0,0] || a == 0)
Run Code Online (Sandbox Code Playgroud)
或者确实
valid a b c = [a,b,c] /= [0,0,0] && [a,b] /= [0,0] && a /= 0
Run Code Online (Sandbox Code Playgroud)无论如何,这些检查都是多余的.如果a不是0那么列表的平等也不可能持有!所以,
valid a b c = a /= 0
Run Code Online (Sandbox Code Playgroud)
也会起作用.实际上你甚至没有使用b和c论证,所以只需写
valid a _ _ = a /= 0
Run Code Online (Sandbox Code Playgroud)
......或者根本就不单独定义valid:简单地内联条件a /= 0.
nRaizes a b c = if (a /= 0) == False then error "not a valid eq"
...
Run Code Online (Sandbox Code Playgroud)
这当然是完全迂回:简单地使用
nRaizes a b c = if a == 0 then error "not a valid eq"
...
Run Code Online (Sandbox Code Playgroud)这仍然会让你if在讨厌的嵌套parens中留下一些丑陋的嵌套.Haskellers不喜欢这样,首选的风格是使用警卫:
nRaizes a b c
| a == 0 = error "not a valid eq"
| b^2 - 4*a*c > 0 = 2
| b^2 - 4*a*c == 0 = 1
| otherwise = 0
Run Code Online (Sandbox Code Playgroud)仍然不是最优的:你计算两次判别式.为什么不:
nRaizes a b c
| a == 0 = error "not a valid eq"
| d > 0 = 2
| d == 0 = 1
| otherwise = 0
where d = b^2 - 4*a*c
Run Code Online (Sandbox Code Playgroud)虽然error 可以这样使用,但我想知道为什么你在那时检查这个.如果a==0那时它不是真正的二阶多项式,那又怎么样?它仍然有许多解决方案.如果所有系数都为零(因为解的数量是无限的),真的应该是错误情况.因此,我认为您真正想要的代码可能如下:
nRaizes :: (Eq a, Floating a) => a -> a -> a -> Int
nRaizes a b c
| all (==0) [a,b,c] = error "Equation has infinite solutions"
| d > 0 = 2
| d == 0 = 1
| otherwise = 0
where d = b^2 - 4*a*c
Run Code Online (Sandbox Code Playgroud)