首页 文章

无法演绎(Num [Char])来自字面意思

提问于
浏览
1

伙计们,我刚刚开始学习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))

一切看起来都不错,但是当我尝试在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.

有人可以向我解释这段代码有什么问题吗?我该如何解决?谢谢

1 回答

  • 6

    正如我已经评论过的那样,在编写任何实际代码之前,您应始终拥有类型签名 . 在实际实现任何内容之前,首先要明确代码的用途是什么!

    所以, valid 取三个数字并以某种方式检查它们,产生 FalseTrue - 即一个布尔值 . 因此,有效的签名将是

    valid :: Int -> Int -> Int -> Bool
    

    这会将参数限制为机器大小的整数 - 快速但不溢出安全 . 它也可能是

    valid :: Integer -> Integer -> Integer -> Bool
    

    或者,对于浮点实数,

    valid :: Double -> Double -> Double -> Bool
    

    实际上,您不需要确定特定类型:它可以是任何数字类型,它只需要支持相等比较 . “正确”的签名将是

    valid :: (Num a, Eq a) => a -> a -> a -> Bool
    

    如果你只是给它没有类型签名的代码,那确实也是GHC的推论:

    Prelude> :t valid
    valid :: (Eq a, Num a) => a -> a -> a -> Bool
    

    但编译器只能自行解决这个问题,因为函数 valid 恰好是类型正确的 . 如果你犯了一些错误,那么编译器不知道该类型应该是什么,因此可能推断出一些导致隐藏错误消息的荒谬类型 . (这只是您应该首先编写签名的原因之一 . )

    这就是 nraized 发生的事情 . 这也需要三个数字并给出一个数字 . 让我们保持简单:

    valid :: Double -> Double -> Double -> Int
    

    这肯定是可以的(虽然你当然可以使它更通用) .

    现在错误消息更加清晰:

    <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))
    

    这告诉你的是 "not a valid eq"Int 类型不兼容 . 实际上非常明显,isn 't it? A function that'应该返回 012 应该不能返回一个字符串!

    如果您确实希望这是一个错误案例,您应该将其标记为:

    nRaizes a b c = if valid a b c == False then error "not a valid eq"
         ...
    

    在这里,字符串不是结果:如果遇到这种情况,程序将被简单地中止并在用户处提示错误消息,而不是试图将其传递给其他函数(这不可能再给出有意义的结果) ,只是更奇怪的错误) .


    几个风格笔记

    • 一般避免在明确提及 TrueFalse 的情况下嵌套 if - 这是不必要的复杂:无论如何,比较会产生布尔值 . valid 如果任何等于持有,则给出错误;这可以写
    valid a b c = if [a,b,c] == [0,0,0]
                   || [a,b] == [0,0]
                   || a == 0
                  then False 
                  else True
    

    ......但那就是一样的

    valid a b c = not ([a,b,c] == [0,0,0] || [a,b] == [0,0] || a == 0)
    

    或者确实

    valid a b c = [a,b,c] /= [0,0,0] && [a,b] /= [0,0] && a /= 0
    
    • 无论如何,这些检查都是多余的 . 如果 a 不是 0 那么列表等号也不可能保持!所以,
    valid a b c = a /= 0
    

    也会起作用 . 实际上你甚至没有使用 bc 参数,所以只需写

    valid a _ _ = a /= 0
    

    ...或者根本不单独定义 valid :只需内联条件 a /= 0 .

    nRaizes a b c = if (a /= 0) == False then error "not a valid eq" 
          ...
    

    这当然是完全迂回:简单地使用

    nRaizes a b c = if a == 0 then error "not a valid eq" 
          ...
    
    • 这仍然让你在讨厌的嵌套parens中留下一些丑陋的嵌套 if . 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
    
    • 仍然不是最优的:你计算两次判别式 . 为什么不:
    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
    

    虽然 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
    

相关问题