如何在Haskell中实现Decimal到Binary函数

时间:2012-02-06 19:39:40

标签: haskell recursion functional-programming binary list-comprehension

我在Haskell中实现了二进制到十进制函数,并且我正在处理一个将十进制转换为二进制值的函数。 (我知道这些功能在某处可用,尽管它们不属于Prelude.hs)

我想出了以下C语言程序语言的代码,但是我无法将其应用到功能范例中。

while (n > 0)
{
    if (n % 2 == 1)
        str = str + "1";
    else
        str = str + "0";
    n = n / 2;
}

我最近才冒险进入Haskell的函数式编程,所以我对功能性思维方式很陌生。我使用递归和列表推导尝试了上述内容,但我不确定如何正确放置警卫和逻辑,因为这涉及多个条件。我使用Int列表来保存单独的二进制位。

--Decimal to binary
toBin:: Int -> [Int]
toBin 0 = [0]
toBin n | (n % 2 == 1) =
        |(n % 2 == 0) = 

我知道上面的模式会让程序选择guard和end来评估函数。我在这里错了吗?

下面是我提出的原始递归,将任何基数(小于10,代替2)转换为十进制。

toDecimal :: [Int] -> Int
toDecimal [] = 0
toDecimal (x:xs) = (x * 2 ^(length xs)) + bin xs

先谢谢。

6 个答案:

答案 0 :(得分:15)

没有%运营商;你可能正在寻找`mod`

toBin 0 = [0]
toBin n | n `mod` 2 == 1 = ...
        | n `mod` 2 == 0 = ...

Guards允许您在函数的多个分支之间进行选择。在这种情况下,如果...的相应条件为真,则toBin n将是++的结果。要将两个列表附加在一起,您可以使用`div`运算符,toBin 0 = [0] toBin n | n `mod` 2 == 1 = toBin (n `div` 2) ++ [1] | n `mod` 2 == 0 = toBin (n `div` 2) ++ [0] 对应于整数除法:

0

然而,这有一些问题。首先,它总是以++ [1]开始结果,这是多余的;另外,使用toBin很慢,因为它必须遍历整个列表才能在结尾添加元素;最好在我们去的时候 prepend 每个元素,然后反转结果。

为了解决这两个问题,我们将toBin 0 = [0] toBin n = reverse (helper n) helper 0 = [] helper n | n `mod` 2 == 1 = 1 : helper (n `div` 2) | n `mod` 2 == 0 = 0 : helper (n `div` 2) 分为主函数和辅助函数:

:

在这个版本中,我们使用toBin运算符,它接受一个值和一个列表,并返回带有前置值的列表。我们还在帮助器中返回0的空结果,并在helper中处理0个案例,以便在结果中不再需要0。

我们可以通过完全跳过警卫来简化n `mod` 2的代码,因为我们只是在右侧再次写出helper 0 = [] helper n = (n `mod` 2) : helper (n `div` 2) 的结果:

div

最后,有一个函数可以一次执行modhelper 0 = [] helper n = let (q,r) = n `divMod` 2 in r : helper q ,这样可以提高效率:

{{1}}

作为补充说明,这并没有真正将十进制转换为二进制,它将整数转换为二进制; Haskell实现不太可能以十进制格式存储整数,尽管它们是以该格式编写和打印的。要将十进制的完整转换写入二进制,将需要一个将十进制字符串解析为整数的函数。

答案 1 :(得分:5)

toBinary :: Int -> [ Int ]

toBinary 0 = [ 0 ]

toBinary n = toBinary ( n `quot` 2 ) ++ [ n `rem` 2 ]

答案 2 :(得分:2)

toBin :: Int -> [Int]
toBin 0 = [0]
toBin 1 = [1]
toBin n
    | n `mod` 2 == 0 = toBin (n `div` 2) ++ [0]
    | otherwise = toBin (n `div` 2) ++ [1]

0和1是微不足道的情况。 如果n是偶数则你应该在末尾追加零,否则你追加一个。 在你的最后一个守卫表达中捕捉所有内容是很好的做法(这就是为什么我使用其他与真实相同的原因)

答案 3 :(得分:0)

您可以使用unfoldr模块中的Data.List功能和intToDigit中的Data.Char

dec2bin :: Int -> [Char]
dec2bin = reverse . map intToDigit . unfoldr (\x -> if x==0 then Nothing else Just(rem x 2, div x 2))

这里发生的是unfoldr函数进程带有提供的匿名函数的输入,直到它返回Nothing,同时聚合来自Just的第一个值一个列表。此列表包含Int s,因此需要使用Char将其转换为intToDigit然后反转,因为它们是按相反顺序收集的。 Char的列表是Haskell中的一个字符串,所以你已经完成了。

答案 4 :(得分:0)

最近,我一直在考虑这个问题,我想出了一个很好的解决方案, 该代码在Haskell中,很简单,但是非常有效的buut仅适用于正数 因为我还不够先进,无法计算说2的补码或其他与二进制代码相关的klike。

binarz :: Int -> [Int]
binarz 0 = []
binarz n = binarz (div n 2) ++ [(mod n 2)] 

答案 5 :(得分:0)

您可以使用Data.Bits中的位移

fromBits :: Bits a => a -> [Bool]
fromBits b
    | b == zeroBits = []
    | otherwise     = testBit b 0 : fromBits (shiftR b 1)

fromBits_ :: Bits a => a -> [Bool]
fromBits_ = reverse . fromBits