Haskell 如何实现十进制到二进制的转换
我在Haskell中实现了一个二进制到十进制的函数,目前正在开发一个将十进制转换为二进制值的函数。(我知道这些功能在某些地方可用,尽管它们不是Prelude.hs的一部分) 我为C型过程语言编写了以下代码,但在将其应用于函数范式时遇到了困难Haskell 如何实现十进制到二进制的转换,haskell,recursion,functional-programming,binary,list-comprehension,Haskell,Recursion,Functional Programming,Binary,List Comprehension,我在Haskell中实现了一个二进制到十进制的函数,目前正在开发一个将十进制转换为二进制值的函数。(我知道这些功能在某些地方可用,尽管它们不是Prelude.hs的一部分) 我为C型过程语言编写了以下代码,但在将其应用于函数范式时遇到了困难 while(n>0) { 如果(n%2==1) str=str+“1”; 其他的 str=str+“0”; n=n/2; } 我最近才在Haskell开始函数式编程,所以我对函数式思维方式很陌生。我尝试使用递归和列表理解来实现上述操作,但我不确定如何正确放
while(n>0)
{
如果(n%2==1)
str=str+“1”;
其他的
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和1是普通情况。
如果n是偶数,那么应该在末尾追加零,否则追加一。
捕捉最后一个guard表达式中的所有内容是一个很好的练习(这就是为什么我使用了与True相同的其他表达式)没有
%
操作符;您可能正在寻找`mod`
:
toBin 0 = [0]
toBin n | n `mod` 2 == 1 = ...
| n `mod` 2 == 0 = ...
通过保护,可以在函数的多个分支之间进行选择。在这种情况下,如果其相应条件为真,则每个..
都将是托宾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
拆分为一个主函数和一个辅助函数:
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)
在这个版本中,我们使用:
操作符,它获取一个值和一个列表,并返回列表,其中的值在列表的开头。我们还将在助手中为0返回一个空结果,并在toBin
中处理0的大小写,以便结果中不超过所需的0
我们可以通过完全跳过防护来简化helper
的代码,因为我们只需在右侧再次写入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
作为补充说明,这并不是真的将十进制转换为二进制,而是将整数转换为二进制;Haskell实现不太可能以十进制格式存储整数,尽管它们是以十进制格式编写和打印的。要编写十进制到二进制的完整转换,需要一个将十进制字符串解析为整数的函数。您可以使用
数据中的展开器函数。List
模块和Data.Char中的intToDigit
:
toBinary :: Int -> [ Int ]
toBinary 0 = [ 0 ]
toBinary n = toBinary ( n `quot` 2 ) ++ [ n `rem` 2 ]
dec2bin :: Int -> [Char]
dec2bin = reverse . map intToDigit . unfoldr (\x -> if x==0 then Nothing else Just(rem x 2, div x 2))
这里发生的是unfover
函数使用提供的匿名函数处理输入,直到返回Nothing
,同时将Just
中的第一个值聚合到列表中。此列表包含Int
s,因此需要使用intToDigit
将它们转换为Char
s,然后进行反转,因为它们是按相反的顺序收集的。Char
s列表是Haskell中的一个字符串,所以您就完成了。所以我最近一直在考虑这个问题,我想出了一个很好的解决方案,
代码是用Haskell编写的,虽然简单但非常有效,但只适用于正数
因为我还没有足够的能力来计算比如说2的补码或者其他类似于二进制代码的东西
binarz :: Int -> [Int]
binarz 0 = []
binarz n = binarz (div n 2) ++ [(mod n 2)]
您可以使用数据的位移位。位
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
“需要一个将十进制字符串解析为整数的函数”-比如,呃,read
?@DanielFischer:是的,但可能OP正在尝试从头开始实现:)毕竟,showIntAtBase
也存在。
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