Sum Lists


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


Right 123
[3,2,1]

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

Tests


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


+++ OK, passed 100 tests.

In [11]:
testNaturalToList :: Natural -> Bool
testNaturalToList x =
    Right x == (listToNatural . naturalToList) x

In [12]:
quickCheck testNaturalToList


+++ OK, passed 100 tests.

In [13]:
testAddLists :: Natural -> Natural -> Bool
testAddLists a b =
    listToNatural (addLists (naturalToList a) (naturalToList b)) == Right (a + b)

In [14]:
testAddLists 95 5


True

In [15]:
quickCheck testAddLists


+++ OK, passed 100 tests.