我在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
先谢谢。
答案 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
最后,有一个函数可以一次执行mod
和helper 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