在SML中从十进制转换为二进制

时间:2014-02-23 15:30:16

标签: sml smlnj

我有以下公式

fun foo 0 = [0] 
  | foo num = let val l =  (num mod 2)::foo(num div 2) in
           rev l
           end;

应该从十进制转换为二进制。它具有以下签名

val foo = fn : int -> int list

我不确定我在哪里弄错了,因为我得到的结果不正确。可能有人帮我弄清楚我在哪里犯了错误?

2 个答案:

答案 0 :(得分:3)

问题似乎是你在每个递归步骤中反转结果列表,而不是在结束时反转一次。

此外,您可能需要将0映射到空列表,否则最终会有一个0太多。

答案 1 :(得分:3)

正是安德烈亚斯所说的。现在,解决这个问题的显而易见的方法是使用包装函数:

fun f n =
let
  fun f' 0   = []
    | f' num = num mod 2 :: f' (num div 2)
in
  rev (f' n)
end

这样可行,但缺点是首先构建列表,然后遍历它(rev调用)。它也不是尾递归的。我们可以做得更好!

我们不是使用反向,而是使用累加器:

fun g n =
let
  fun g' 0   acc = acc
    | g' num acc = g' (num div 2) (num mod 2 :: acc)
in
  g' n []
end

为了理解差异,让我们看看如果我们在数字4上运行每一个会发生什么。

f 4 -> rev (f' 4)
    -> rev (4 mod 2 :: f' (4 div 2))
    -> rev (0 :: f' 2)
    -> rev (0 :: 2 mod 2 :: f' (2 div 2))
    -> rev (0 :: 0 :: f' 1)
    -> rev (0 :: 0 :: 1 mod 2 :: f' (1 div 2))
    -> rev (0 :: 0 :: 1 :: f' 0)
    -> rev (0 :: 0 :: 1 :: [])
    -> [1, 0, 0]

g 4 -> g' 4 []
    -> g' (4 div 2) (4 mod 2 :: [])
    -> g' 2 (0 :: [])
    -> g' (2 div 2) (2 mod 2 :: 0 :: [])
    -> g' 1 (0 :: 0 :: [])
    -> g' (1 div 2) (1 mod 2 :: 0 :: 0 :: [])
    -> g' 0 (1 :: 0 :: 0 :: [])
    -> [1, 0, 0]