【问题标题】:Beginner Haskell problem - Couldn't match type ‘Bool’ with ‘[Char]’初学者 Haskell 问题 - 无法将类型“Bool”与“[Char]”匹配
【发布时间】:2020-02-03 14:41:47
【问题描述】:

我需要在 Haskell 中使用递归来制作一个回文检查器来完成家庭作业。该函数应该接受一个字符串并返回一个Bool。尝试编译时,出现错误,"Couldn't match type ‘Bool’ with ‘[Char]’."

我对 Haskell 很陌生,所以我确定我只是忽略了一些愚蠢的事情,但我想我还是会寻求一些帮助。我在下面粘贴了我的代码,以及我收到的完整错误。

isPalindrome :: String -> Bool
isPalindrome s
  | isPalindrome ((null s) || (length s == 1)) = True
  | isPalindrome ((s !! 0) /= (s !! (length s - 1))) = False
  | otherwise = isPalindrome (tail (init s))

我的实现检查输入字符串是否为空或大小为 1,如果是则返回 true。如果不是,则检查第一个字符和最后一个字符是否不同,如果不同,则返回 false。否则,它会再次调用自身,传入字符串中第一个和最后一个字符被截断。

main.hs:15:19: error:
    • Couldn't match type ‘Bool’ with ‘[Char]’
      Expected type: String
        Actual type: Bool
    • In the first argument of ‘isPalindrome’, namely
        ‘((null s) || (length s == 1))’
      In the expression: isPalindrome ((null s) || (length s == 1))
      In a stmt of a pattern guard for
                     an equation for ‘isPalindrome’:
        isPalindrome ((null s) || (length s == 1))
   |
15 |   | isPalindrome ((null s) || (length s == 1)) = True
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
main.hs:16:19: error:
    • Couldn't match type ‘Bool’ with ‘[Char]’
      Expected type: String
        Actual type: Bool
    • In the first argument of ‘isPalindrome’, namely
        ‘((s !! 0) /= (s !! (length s - 1)))’
      In the expression: isPalindrome ((s !! 0) /= (s !! (length s - 1)))
      In a stmt of a pattern guard for
                     an equation for ‘isPalindrome’:
        isPalindrome ((s !! 0) /= (s !! (length s - 1)))
   |
16 |   | isPalindrome ((s !! 0) /= (s !! (length s - 1))) = False
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

【问题讨论】:

    标签: haskell type-mismatch guard-clause


    【解决方案1】:

    f x 是对函数f 的函数调用,参数为x。但是你不需要调用那个函数,如果测试表达式x 已经是你所需要的:

    isPalindrome :: String -> Bool
    isPalindrome s
      --   f               x
      | {- isPalindrome -} ((null s) || (length s == 1))        -- here
                  = True
      | {- isPalindrome -} ((s !! 0) /= (s !! (length s - 1)))  -- and here
                  = False
      | otherwise = isPalindrome (tail (init s))  
    

    isPalindrome :: String -> Bool 表示isPalindrom 的第一个参数应该是String。但真正的意义在于有一个布尔值,用作保护,以选择适当的行动方案。因此出现错误消息。我们已经有了Bool

    最后一行的函数调用是确实必须做的递归调用。

    {- ... -} 是 Haskell 中的多行 cmets。


    Haskell 更惯用的常用方法是不显式执行这些测试,而是通过子句在函数定义中安排等效的模式匹配:

    isPalindrome :: String -> Bool
    isPalindrome []     = True           -- (null s)
    isPalindrome [_]    = True           -- (length s == 1)
    isPalindrome -- [a, ..., b] | a /= b
                 (x:xs) | x /= last xs
                        = False          --  ((s !! 0) /= (s !! (length s - 1))) 
    isPalindrome -- [a, ...xs, b] 
                 (_:xs) = isPalindrome   --  xs
                                       (init xs)
    

    上面的代码在 cmets 中包含了一些虚构的列表模式,在代码本身中包含了它们的 Haskell 等价物。

    事实上,正如@chepner 在 cmets 中指出的那样,模式通常可以帮助避免效率低下:计算 lengthO(n) 而与 [_] 匹配的模式是 O(1)

    当然,这个特定的代码仍然非常低效,因为其他两个子句也执行 O(n) 操作(lastinit)。存在 O(n) 算法。

    【讨论】:

    • 另一个更喜欢模式匹配的原因:如果你从一个非常大的列表开始,当你只关心它的长度时,你不需要花费 O(n) 时间来确定列表的实际长度是
    • @MC3D 不客气。 :) 所以这确实是“一些简单的事情”——不需要将这些布尔值用作任何函数的参数,而只需在守卫中使用它们即可。
    猜你喜欢
    • 1970-01-01
    • 2022-01-23
    • 2021-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多