Haskell十进制转换为二进制

时间:2019-01-26 11:48:59

标签: haskell binary decimal

我正在尝试构建一个将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;
        }       }
}

希望这将有助于找到解决方案!

5 个答案:

答案 0 :(得分:8)

您的解决方案存在一些问题。首先,我建议不要完全使用do ,直到您了解do的作用。这里我们根本不需要do

  

不幸的是,除了在Java中,不可能在haskell中将int除以2。

实际上是,但是/运算符(实际上是(/)函数)的类型为(/) :: Fractional a => a -> a -> aInt不是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只能产生两个结果:01,而这些正是我们在(:)运算符左侧使用的结果。

因此我们根本不需要使用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