Reputation: 29
I try to add numbers represented by a list, for example:
0 = []
1 = [1]
2005 = [2005]
123456 = [3456; 12]
1234567890 = [7890; 3456; 12]
1000000001 = [1; 0; 10]
so add [9999] [1] = [0;1]
I've done this:
let rec add l1 l2 =
match l1, l2 with
| n::l1', m::l2' -> if n+m < 10000 then
(n+m)::(add l1' l2')
else
begin
match l1', l2' with
| p::l1'', q::l2'' -> ((n+m) - 10000)::(add ((1+p)::l1'') l2')
| p::l1'', [] -> ((n+m) - 10000)::(add ((1+p)::l1'') l2')
| [], q::l2'' -> ((n+m) - 10000)::(add l1' ((1+q)::l2''))
| [], [] -> ((n+m) - 10000)::[1]
end
| [], [] -> []
| m::l1', [] -> m::(add l1' [])
| [], m::l2' -> m::(add [] l2')
but it doesn't work with something like add [9999;9999] [1]
which gives [0; 10000]
instead of [0;0;1]
.
There is a problem with the carry, but I can't find it.
It works with add [8500;6000] [5600;452]
.
Upvotes: 2
Views: 153
Reputation: 14750
Let's have a look at the issue in your code. I have put my explanations of the problem in the comments:
let rec add l1 l2 =
match l1, l2 with
| n::l1', m::l2' -> if n+m < 10000 then
(n+m)::(add l1' l2')
else
begin
(* in all these cases you don't check if 1+p is > base *)
match l1', l2' with
| p::l1'', q::l2'' -> ((n+m) - 10000)::(add ((1+p)::l1'') l2')
| p::l1'', [] -> ((n+m) - 10000)::(add ((1+p)::l1'') l2')
| [], q::l2'' -> ((n+m) - 10000)::(add l1' ((1+q)::l2''))
| [], [] -> ((n+m) - 10000)::[1]
end
| [], [] -> []
(* and the error above causes problem in these arms, since m can now be > base *)
| m::l1', [] -> m::(add l1' [])
| [], m::l2' -> m::(add [] l2')
Let's put the special cases first, replace the literal constant with a value, remve superfluous parentheses and fix the carry mistake.
let rec add l1 l2 base =
match l1, l2 with
| [], [] -> []
| m::l1', []
| [], m::l1' -> if m >= base then m-base::add l1' [1] base else m::l1'
| n::l1', m::l2' -> let r = n + m in
if r < base then
r::add l1' l2' base
else begin
match l1', l2' with
| [], [] -> [r-base ; 1]
| p::l1'', []
| [], p::l1'' -> r-base::add [] (1+q::l1'') base
| p::l1'', _ -> r-base::add (1+p::l1'') l2' base
end
Now we can see that a bit of code could be extracted and turned into a dedicated function:
let rec add l1 l2 base =
let rec carry = function
| [] -> []
| [x] when x >= base -> [x-base;1]
| x::y::l when x >= base -> x-base::carry (y+1::l)
| l -> l
in
match l1, l2 with
| [], [] -> []
| l1, []
| [], l1 -> carry l1
| n::l1', m::l2' -> let r = n + m in
if r < base then
r::add l1' l2' base
else begin
match l1', l2' with
| [], [] -> [r-base ; 1]
| l1'', []
| [] , l1'' -> carry (r::l1'')
| p::l1'', _ -> r-base::add (1+p::l1'') l2' base
end
Now your code should work with the carry preserved, and as a bonus be able to work with any natural base.
Upvotes: 0
Reputation: 36118
The problem is with your denormalized representation, which can temporarily create digits larger than 9999, which you don't expect in all places. Here is a simple solution with explicit carry that avoids that issue:
let base = 10_000
let rec add' xs ys c =
match xs, ys with
| [], [] -> if c = 0 then [] else [c]
| [], zs | zs, [] -> add' [0] zs c
| x::xs', y::ys' -> (x + y + c) mod base :: add' xs' ys' ((x + y + c) / base)
let add xs ys = add' xs ys 0
Upvotes: 2
Reputation: 66823
It looks to me like your cases assume that digits are always less than or equal to 9999, but this isn't true when there is a carry. So you need to check against 10000 even when one of the lists is empty.
Upvotes: 4