决定(在Haskell中)数字是否是不使用列表的回文

时间:2014-10-11 14:52:21

标签: list haskell palindrome declarative

我需要检查Haskell,如果四位数是一个回文,问题是我不能使用列表,尽管有一个固定的数字,我应该使用递归。我一直在思考这个问题,但是我无法使用递归来获得解决方案。我能得到的最接近的是:

pldrm :: Integer -> Bool
pldrm x
    |x > 9999 = False
    |x > 999 = (div x 1000 == mod x 10) && (mod (div x 100) 10) == div (mod x 100) 10
    |otherwise = False
你知道吗?感谢

5 个答案:

答案 0 :(得分:12)

如何检查数字是否等于其反转?

palindrome :: Integer -> Bool
palindrome x = reversal x == x

reversal :: Integral a => a -> a
reversal = go 0
  where go a 0 = a
        go a b = let (q,r) = b `quotRem` 10 in go (a*10 + r) q

这样可以让-121这样的负数成为回文,如果你不想这样做,很容易检查。

nonNegativePalindrome x = x >= 0 && palindrome x

reversal给出一个整数,其数字与输入的顺序相反(忽略12 == ...000012中隐含的无限前导零)。

reversal通过剥离底部的数字(使用quotRemwhich is a lot like divMod)并以相反的顺序(通过多重复制和添加)将它们组合在一起。

reversal 12345
= go 0 12345 
= go 5  1234
= go 54  123
= go 543  12
= go 5432  1
= go 54321 0
= 54321

值得注意的是n == reversal $ reversal n仅当n为零或具有非零的1位数时。 (reversal (reversal 1200) == 12),但reversal范围内的整数都是可逆的:reversal x == reversal (reversal (reversal x)) forall x

有关如何获得此解决方案的更详尽说明in this blog post

答案 1 :(得分:7)

好吧,这确实比Haskell有点棘手且算法更多,所以让我们看看一个可能的解决方案(假设一个十进制系统)。

我们的想法是使用divmod来获取数字的最高位和最低位。 请记住,你可以写

(q,r) = n `divMod` m

获取数字qr,以便q * m + r = n获得0 <= r < q。对于m = 10这个 将方便地得到(正面n):

  • q除了最后的数字
  • 之外的所有内容
  • r最后一位数字

评论:我有一段时间错了 - 我希望现在是正确的 - 边缘情况真的很棘手。

palindrome :: Integer -> Bool
palindrome n = palin n (digits n)
  where
    palin x dgts
      | x < 0     = False
      | x == 0    = True
      | x < 10    = dgts == 1
      | otherwise = q == x `mod` 10 && palin inner (dgts-2)
      where
        inner = r `div` 10
        (q,r) = x `divMod` size
        size  = 10^(dgts-1)

digits :: Integer -> Integer
digits x
  | x < 10    = 1
  | otherwise = 1 + digits (x `div` 10)

很明显我不知道问题的大小,所以digits会查找位数:

  • 位数5445 = 4
  • 位数123 = 3
  • ...

边缘情况如下:

  | x < 0     = False
  | x == 0    = True
  | x < 10    = digits == 1
  • 明显的负数不应该是回文
  • 如果所有数字都是0那么它就是一个回文
  • 一位数的数字是回文,如果我们确实只看到长度为1(这让我很糟糕,因为像inner这样的1011是一位数的nubmer 1

其余的是基于这些观察结果:

  • x div 10^(digits-1) =最高位(5445 div 1000 = 5
  • x mod 10^(digits-1) =除最高位(5445 mod 1000 = 445
  • 以外的所有数字
  • x mod 10 =最低位(5445 mod 10 = 5
  • number div 10 =删除最低位(5445 div 10 = 544

为了安全起见,让我们使用Quickcheck进行测试:

让我们使用Quickcheck来测试它(应该是一个很好的例子:D)

module Palindrome where

import Test.QuickCheck

main :: IO ()
main = do
  checkIt palindrome

palindrome :: Integer -> Bool
palindrome n = palin n (digits n)
  where
    palin x dgts
      | x < 0     = False
      | x == 0    = True
      | x < 10    = dgts == 1
      | otherwise = q == x `mod` 10 && palin inner (dgts-2)
      where
        inner = r `div` 10
        (q,r) = x `divMod` size
        size  = 10^(dgts-1)

digits :: Integer -> Integer
digits x
  | x < 10    = 1
  | otherwise = 1 + digits (x `div` 10)

checkIt :: (Integer -> Bool) -> IO ()
checkIt p =
  quickCheckWith more (\n -> n < 0 || p n == (reverse (show n) == show n))
  where more = stdArgs { maxSuccess = 10000, maxSize = 999999 }

似乎没问题:

runghc Palindrom.hs 
+++ OK, passed 10000 tests.

答案 2 :(得分:6)

如果仅考虑四位数,您可以递归减去1001以检查第一个和最后一个数字是否相等,然后减去0110以检查中间数字是否相等。

pldrm :: Int -> Bool
pldrm x
  | x > 1000 = pldrm (x - 1001)
  | x > 100  = pldrm (x - 110)
  | otherwise = x == 0

请注意,此功能会为[1000,9999]范围以外的数字提供不正确的结果。

答案 3 :(得分:3)

很遗憾你不能使用清单。这是基于算术运算的繁琐解决方案(仅适用于四位数字):

pldrm :: Int -> Bool -- no need for Integer if you work only with four
                     -- digit numbers
pldrm x = (div x 1000 == mod x 10) && (div y 10 == mod y 10)
    where y = rem x 1000 `quot` 10 -- extracts two inner digits

> pldrm 3113
True
> pldrm 3111
False

答案 4 :(得分:0)

isPolindrom :: Integer -> Bool 
isPolindrom n = if let i = read (reverse (show n)) :: Integer in i==n then True else False