Ok, this is indeed a bit tricky and more math than Haskell so let's look at a possible solution (assuming a decimal system).
The idea is to use div
and mod
to get at the highest and lowest digit of a number.
Remember that you can write
(q,r) = n `divMod` m
to get numbers q
and r
so that q * m + r = n
with 0 <= r < q
. For m = 10
this
will conveniently get (for positive n
):
- in
q
all but the last digits
- in
r
the last digit
remark: I had this wrong for some time - I hope it's correct now - the edge cases are really tricky.
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)
Obvious I did not know the size of your problem so digits
will look for the number of digits:
- digits 5445 = 4
- digits 123 = 3
- ...
The edge cases are these:
| x < 0 = False
| x == 0 = True
| x < 10 = digits == 1
- Obvious negative numbers should not be palindromes
- if all digits are 0 then it's an palindrome
- one-digit numbers are palindromes if indeed we are looking only at length 1 (this had me bad, as the
inner
of stuff like 1011
is a one digit nubmer 1
)
The rest is based on this observations:
x div 10^(digits-1)
= the highest digit (5445 div 1000 = 5
)
x mod 10^(digits-1)
= all but the highest digit (5445 mod 1000 = 445
)
x mod 10
= the lowest digit (5445 mod 10 = 5
)
number div 10
= remove the lowest digit (5445 div 10 = 544
)
just to be safe let's test it using Quickcheck:
Let's use Quickcheck to test it (should be a nice example :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 }
seems ok:
runghc Palindrom.hs
+++ OK, passed 10000 tests.
Traversable
instance of the number, i guess. Then just compare the backward traversed number with the regular traversed number. – Burkettmono-traversable
for that reason. – Chantalchantalle