初学者 Haskell 问题 - 无法将类型“Bool”与“[Char]”匹配

Beginner Haskell problem - Couldn't match type ‘Bool’ with ‘[Char]’

我需要在 Haskell 中使用递归制作一个回文检查器来完成家庭作业。该函数应该接受一个字符串和 return 一个 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,如果是,return为真。如果不是,它会检查第一个字符和最后一个字符是否不同,如果不同,returns 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
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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。但真正的意思是有一个布尔值,用作守卫,select 适当的行动过程。因此出现错误消息。我们已经有了 Bool.

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

{- ... -} 是多行注释,在 Haskell.


通常更惯用的 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)

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

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

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