In [1]:
import Numeric.Natural
In [2]:
listToNatural :: [Natural] -> Either String Natural
listToNatural [] = Right 0
listToNatural [_, 0] = Left "trailing zeros"
listToNatural (x:xs)
| x > 9 = Left "digit outside [0..9]"
| otherwise = fmap ((x +) . (10 *)) (listToNatural xs)
In [3]:
naturalToList :: Natural -> [Natural]
naturalToList 0 = [0]
naturalToList x = l:ist
where
l = x `mod` 10
x' = x `div` 10
ist = if x' /= 0 then naturalToList x' else []
In [4]:
listToNatural [3, 2, 1]
naturalToList 123
In [5]:
{-# LANGUAGE ScopedTypeVariables #-}
In [6]:
addLists :: [Natural] -> [Natural] -> [Natural]
addLists xs ys = addLists' xs ys 0
where
addLists' :: [Natural] -> [Natural] -> Natural -> [Natural]
addLists' [] [] 0 = []
addLists' [] [] carry = [carry]
addLists' as [] carry = addLists' as [0] carry
addLists' [] bs carry = addLists' bs [] carry
addLists' (a:as) (b:bs) carry = d' : addLists' as bs carry'
where
d = a + b + carry
d' = d `mod` 10
carry' = d `div` 10
In [7]:
import Test.QuickCheck
In [8]:
import Data.Either (isLeft)
In [9]:
testListToNatural :: [Natural] -> Bool
testListToNatural [] = listToNatural [] == Right 0
testListToNatural xs
| any (> 9) xs = isLeft x
| length xs > 1 && last xs == 0 = isLeft x
| otherwise = either (const False) ((xs ==) . naturalToList) x
where
x = listToNatural xs
In [10]:
quickCheck testListToNatural
In [11]:
testNaturalToList :: Natural -> Bool
testNaturalToList x =
Right x == (listToNatural . naturalToList) x
In [12]:
quickCheck testNaturalToList
In [13]:
testAddLists :: Natural -> Natural -> Bool
testAddLists a b =
listToNatural (addLists (naturalToList a) (naturalToList b)) == Right (a + b)
In [14]:
testAddLists 95 5
In [15]:
quickCheck testAddLists