【问题标题】:Converting Decimal to Binary in Haskell在 Haskell 中将十进制转换为二进制
【发布时间】:2016-10-21 22:17:01
【问题描述】:

我发现这个 sn-p 的代码有效,但我不明白为什么会这样。它将 Int 转换为其二进制表示形式。

repBinario::Int -> Int
repBinario 0 = 0
repBinario x = 10 * repBinario (x `div` 2) + x `mod` 2

我知道 divmod 做什么。但是,它如何将来自mod 的每个数字放在一起?

【问题讨论】:

  • 我不明白这个问题。
  • @melpomene 我想知道每次调用repBinario 时发生了什么,这种递归对于像我这样的新手来说有点混乱。不过,我们下面的答案真的很有用。
  • 我们非常有帮助的答案,我没有只选择一个,因为有些人可能不会寻找与我相同的东西,无论如何,我决定对每个回复进行投票,因为它们都是结构化的以不同的方式解决问题,因此问题导致了不同的解决方案,所有这些都同样有趣。谢谢。

标签: haskell recursion binary


【解决方案1】:

简而言之,它在每次迭代时将累加结果乘以10

为了更清楚地了解正在发生的事情,我们可以将您的函数分成两个更简单的函数。第一个将整数转换为二进制数字列表。然后另一个会做让你烦恼的事情:将二进制数字列表连接成一个整数。

extractBinDigits :: Int -> [Int]
extractBinDigits =
  unfoldr (\x -> if x == 0 then Nothing else Just (mod x 2, div x 2))

concatDigits :: [Int] -> Int
concatDigits =
  foldr (\a b -> a + b * 10) 0

如您所见,我们只需在每个步骤中将累加器乘以 10 并将每个数字都添加到列表中。

那么你原来的功能就变成了这样:

repBinario :: Int -> Int
repBinario =
  concatDigits . extractBinDigits

现在,Division 允许我们检查和重用程序中更精细的部分,从而为我们提供更大的灵活性。例如,通过添加另一个简单的函数,您现在可以一次将整数转换为字符串:

showDigits :: [Int] -> String
showDigits =
  reverse . map (chr . (+ 48))

repStringyBinario :: Int -> String
repStringyBinario =
  showDigits . extractBinDigits

【讨论】:

    【解决方案2】:

    我们来看一个例子,那么:

    repBinario 5
    

    repBinario 5的替代定义:

    10 * repBinario (5 `div` 2) + 5 `mod` 2
    

    减少divmod

    10 * repBinario 2 + 1
                        ^
    

    这里我们生成了第一个数字,标记为^

    repBinario 2的替代定义:

    10 * (10 * repBinario (2 `div` 2) + 2 `mod` 2) + 1
                                                     ^
    

    减少divmod

    10 * (10 * repBinario 1 + 0) + 1
                              ^    ^
    

    repBinario 1的替代定义:

    10 * (10 * (10 * repBinario (1 `div` 2) + 1 `mod` 2) + 0) + 1
                                                           ^    ^
    

    减少divmod

    10 * (10 * (10 * repBinario 0 + 1) + 0) + 1
                                    ^    ^    ^
    

    repBinario 0的替代定义:

    10 * (10 * (10 * 0 + 1) + 0) + 1
                         ^    ^    ^
    

    减少:

    101
    

    在每一步,(`mod` 2) 获取最低有效二进制数字,(`div` 2) 将数字右移,丢弃该数字并将其余数字递归传递给 divBinario。最后,我们执行相反的过程:(+ d) 将当前数字添加到结果中,(* 10) 将数字向左移动以便我们可以添加更多数字。

    你得到的是一个十进制数,看起来与原始输入的二进制表示相同。

    如果你去掉与10 的乘法,你会得到popCount,这个函数可以为你提供一个数字的人口计数——在其二进制表示中1 的位数:

    popCount 0 = 0
    popCount x = popCount (x `div` 2) + x `mod` 2
    
    popCount 5  ==  2
    

    【讨论】:

      【解决方案3】:

      我认为最好手动计算这个函数的一个小值 - 这是可能的,因为这是一个纯函数,因此你可以用它的定义(即右手边)替换左侧 - 花哨的计算机科学此功能的词是“参考透明度”。

      repBinario 24 = 10 * repBinario (24 `div` 2) + 24 `mod` 2
                    = 10 * repBinario 12 + 0
                    = 10 * (10 * repBinario (12 `div` 2) + 12 `mod` 2)
                    = 100 * repBinario 6 + 0
                    = 100 * (10 * repBinario (6 `div` 2) + 6 `mod` 2)
                    = 1000 * repBinario 3 + 0
                    = 1000 * (10 * repBinario (3 `div` 2) + 3 `mod` 2)
                    = 10000 * repBinario 1 + 1000 * 1
                    = 10000 (10 * repBinario (1 `div` 2) + 1 `mod` 2) + 1000
                    = 10000 (10 * repBinario 0 + 1) + 1000
                    = 10000 (10 * 0 + 1) + 1000
                    = 10000 * 1 + 1000
                    = 11000
      

      在这些步骤中,我只是根据函数的定义来评估函数,并使用整数加法/乘法遵守分布规律这一事实。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-26
        • 1970-01-01
        • 1970-01-01
        • 2015-06-02
        • 2011-07-09
        • 2016-03-16
        • 2012-12-26
        相关资源
        最近更新 更多