我正在尝试构建一个将Decimal(Int)转换为二进制数的函数。 不幸的是,除了在Java中,不可能在haskell中将int除以2。 我对函数式编程非常陌生,因此问题可能很简单。 到目前为止,我找不到该问题的另一种解决方案,但是 这是我的第一次尝试:
fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = if (mod n 2 == 0) then
do
0:fromDecimal(n/2)
else
do
1:fromDecimal(n/2)
我在这里有一个Java实现,以前是这样做的:
public void fromDecimal(int decimal){
for (int i=0;i<values.length;i++){
if(decimal % 2 = 0)
values[i]=true ;
decimal = decimal/ 2;
else {values[i]= false;
} }
}
希望这将有助于找到解决方案!
答案 0 :(得分:8)
您的解决方案存在一些问题。首先,我建议不要完全使用do
,直到您了解do
的作用。这里我们根本不需要do
。
不幸的是,除了在Java中,不可能在haskell中将int除以2。
实际上是,但是/
运算符(实际上是(/)
函数)的类型为(/) :: Fractional a => a -> a -> a
。 Int
不是Fractional
。您可以使用div :: Integral a => a -> a -> a
执行整数除法。
因此,代码如下:
fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = if (mod n 2 == 0) then 0:fromDecimal (div n 2) else 1:fromDecimal (div n 2)
但是我们绝对可以使它更加优雅。 mod n 2
只能产生两个结果:0
和1
,而这些正是我们在(:)
运算符左侧使用的结果。
因此我们根本不需要使用if
-then
-else
:
fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = mod n 2 : fromDecimal (div n 2)
也许这仍然不是您想要的:这里我们写二进制值,使得最后一个元素是最重要的一个。此函数将添加一个 tailing 零,这不会造成语义上的差异(由于该顺序),但也不美观。
如果给定值不为零,我们可以定义一个函数go
忽略该零,例如:
fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = go n
where go 0 = []
go k = mod k 2 : go (div k 2)
但是,如果我们想首先写入最高有效位(因此与写入十进制数字的顺序相同),那么我们必须反转结果。我们可以使用累加器
来做到这一点fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = go n []
where go 0 r = r
go k rs = go (div k 2) (mod k 2:rs)
答案 1 :(得分:4)
您无法在Haskell中使用/
个整数-除法不是根据整数定义的!对于积分除法,请使用div
函数,但更适合的是divMod
免费提供的mod
。
另外,您将获得反向输出,因此您可以在此之后手动reverse
,或将更多内存效率的版本与累加器一起使用:
decToBin :: Int -> [Int]
decToBin = go [] where
go acc 0 = acc
go acc n = let (d, m) = n `divMod` 2 in go (m : acc) d
go
将为您提供0
的空白列表。如果列表为空,则可以手动添加它:
decToBin = (\l -> if null l then [0] else l) . go [] where ...
答案 2 :(得分:1)
仔细研究算法的工作方式。它从2⁰开始,因此它将生成比我们通常认为的要低的位,即最低有效位在前。您的算法只能表示非负二进制整数。
fromDecimal :: Int -> [Int]
fromDecimal d | d < 0 = error "Must be non-negative"
| d == 0 = [0]
| otherwise = reverse (go d)
where go 0 = []
go d = d `rem` 2 : go (d `div` 2)
在Haskell中,当我们反向生成列表时,请继续进行操作,但最后要reverse
。这样做的原因是要整理一个列表(将:
粘贴在最前面的新项)的成本是恒定的,而reverse
的末尾是线性的成本,但要附加++
费用是二次方。
常见的Haskell样式应具有一个名为go
的私有内部循环,外部函数对参数满意时将应用该内部循环。基本情况是在d
达到零时以空列表终止。否则,我们将当前余数取模2,然后将d
减半并截断。
没有零的特殊情况,fromDecimal 0
将是空列表,而不是[0]
。
答案 3 :(得分:1)
二进制数通常是字符串,在计算中并未真正使用。 字符串也不太复杂。
二进制数字的模式与其他任何模式一样。它会重复,但是会以更快的速度播放。 只需要一小组即可生成最多256(0-255)个二进制数。 该模式可以系统地扩展更多。 起始模式为4,0-3
bd = ["00","01","10","11"]
将它们组合成更大数字的功能是
d2b n = head.drop n $ [ d++e++f++g | d <- bd, e <- bd, f <- bd, g <- bd]
d2b 125
“ 01111101”
如果不清楚如何扩展,那么
bd = ["000","001","010","011","100","101","110","111"]
最多可以提供4096个二进制数字(0-4095)。其他所有都保持不变。
如果不明显,db2
函数使用4对二进制数,因此使用4对二进制数。 (2 ^ 8)-1或(2 ^ 12)-1是您获得的数量。
顺便说一下,列表理解是糖衣的do
结构。
使用以下方式生成上述模式
[ a++b | a <- ["0","1"], b <- ["0","1"] ]
[“ 00”,“ 01”,“ 10”,“ 11”]
和
[ a++b++c | a <- ["0","1"], b <- ["0","1"], c <- ["0","1"] ]
[“ 000”,“ 001”,“ 010”,“ 011”,“ 100”,“ 101”,“ 110”,“ 111”]
通常,一种模式和一种功能可以达到目的
b2 = ["0","1"]
b4 = [ a++b++c++d | a <- b2, b <- b2, c <- b2, d <- b2]
b4
[“ 0000”,“ 0001”,“ 0010”,“ 0011”,“ 0100”,“ 0101”,“ 0110”,“ 0111”,“ 1000”,“ 1001”,“ 1010”,“ 1011” “,” 1100“,” 1101“,” 1110“,” 1111“]
bb n = head.drop n $ [ a++b++c++d | a <- b4, b <- b4, c <- b4, d <- b4]
bb 32768
“ 1000000000000000”
bb 65535
“ 1111111111111111”
使用减法直接在Haskell中从十进制计算二进制
cvtd n (x:xs) | x>n = 0:(cvtd n xs)
| n>x = 1:(cvtd (n-x) xs)
| True = 1:[0|f<-xs]
使用任意数量的位,例如10位。
cvtd 639 [2^e|e<-[9,8..0]]
[1,0,0,1,1,1,1,1,1,1,1]
答案 4 :(得分:0)
import Data.List dec2bin x = reverse $ binstr $ unfoldr ndiv x where binstr = map (\x -> "01" !! x) exch (a,b) = (b,a) ndiv n = case n of 0 -> Nothing _ -> Just $ exch $ divMod n 2