使用ocaml在基数10.000中添加数字

时间:2012-11-22 20:54:43

标签: ocaml

我尝试添加列表所代表的数字,例如:

0 = []
1 = [1]
2005 = [2005]
123456 = [3456; 12]
1234567890 = [7890; 3456; 12]
1000000001 = [1; 0; 10]

所以add [9999] [1] = [0;1]

我做到了这一点:

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')

但它不适用于add [9999;9999] [1]之类的内容 它提供[0; 10000]而不是[0;0;1]

随身携带有问题,但我找不到。

适用于add [8500;6000] [5600;452]

3 个答案:

答案 0 :(得分:4)

在我看来,你的情况假设数字总是小于或等于9999,但是当有进位时这不是真的。因此,即使其中一个列表为空,您也需要检查10000。

答案 1 :(得分:2)

问题在于你的非规范化表示,它可以暂时创建大于9999的数字,这是你在所有地方都不会想到的。这是一个带有显式进位的简单解决方案,可以避免这个问题:

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

答案 2 :(得分:0)

让我们看一下代码中的问题。我在评论中对问题进行了解释:

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 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

现在我们可以看到可以提取一些代码并将其转换为专用函数:

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

现在您的代码应该与保留的进位一起使用,并且作为奖励可以使用任何自然基础。