我不明白这个从 base-256 转换为十进制的 ruby 代码



github中的代码

代码的目的很简单:将base-256字节字符串转换为base-10

def debase256(string)
  string.reverse.bytes.inject([0, 1]) do |(sum, pow), byte|
    [pow * byte.ord, pow * 256]
  end.first
end

我试图阅读它,但我只走了'reversever.bytes'

我无法想象在此过程中的字节如何移动和变化。

解释这就是我所需要的一个例子。

代码是错误的。这不是在做总和。块中的第一个数组项目应为sum + pow * byte.ord。另外,让byte.ord毫无意义,因为Integer#ord只是返回。

因此,正确的代码将是:

def debase256(string)
  string.reverse.bytes.inject([0, 1]) do |(sum, pow), byte|
    [sum + pow * byte, pow * 256]
  end.first
end

此代码很难遵循。也许以下代码(没有方法声明(可以帮助您更好地理解它:

string.reverse.bytes.map.with_index do |byte, i|
  byte * 256**i
end.sum

让我们看看字符串"Test"的示例:

string = "Test"

首先,我们将其逆转:

string.reverse # => "tseT"

然后我们得到字节:

string.reverse.bytes # => [116, 115, 101, 84]

现在,我们要从此基础256数字构建一个基本10号。我们通过将每个插槽索引i256^i乘以i在0。

开始来做到这一点。
"Test".reverse.bytes.map.with_index { |byte, i| byte * 256**i }
# => [116 * 256^0, 115 * 256^1, 101 * 256^2, 84 * 256^3]
# => [116 * 1, 115 * 256, 101 * 65536, 84 * 16777216]
# => [116, 29440, 6619136, 1409286144]

最后,我们以总和为基础10表示。

"Test".reverse.bytes.map.with_index { |byte, i| byte * 256**i }.sum
# => 1415934836

为了了解我们在做什么,让我们尝试使用基本10到基本10转换的同一件事。假设我们在基本10中有一个数字,例如1234。我们得到了这一点的数字:

1234.digits
# => [4, 3, 2, 1]

注意#digits如何返回数字逆转。

现在,在基本10中,每个插槽i都需要乘以10^i(与基本256中的256^i相比(:

1234.digits.map.with_index { |byte, i| byte * 10**i }
# => [4 * 10^0, 3 * 10^1, 2 * 10^2, 1 * 10^3]
# => [4 * 1, 3 * 10, 2 * 100, 1 * 1000]
# => [4, 30, 200, 1000]

总结将为我们提供10个数字:

1234.digits.map.with_index { |byte, i| byte * 10**i }.sum
# => 1234

因此,唯一的区别是基础,逻辑是相同的。


您可能遇到的另一个例子是十六进制中的RGB颜色值,例如#ac4fbe。对于红色,绿色和蓝色,我们的值在十六进制中编码为0到255。十六进制是基础16的奇特词。通常,十六进制的数字表示为09,而af

0 1 2 3 4 5 6 7 8 9  a  b  c  d  e  f
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

知道这一点,让我们看一下颜色#ac4fbe的红色值,该值由第一个字符 ac表示。

这里的逻辑与上述相同。逆转这给了我们ca。如果我们获得每个字符的基本10号,那就是[12, 10]。让我们将每个插槽乘以16^i

[12 * 16^0, 10 * 16^1] == [12 * 1, 10 * 16] == [12, 160]

总和12 + 160172,它是颜色中红色组件的值。

再次,它与其他示例中的逻辑相同。


我希望这些示例可以帮助您了解这是如何工作的。作为练习,请尝试将此二进制(即基数2(编号转换为基础10:

101010

记住,这些是插槽:

digits: 1 0 1 0 1 0
slot i: 5 4 3 2 1 0

(提示:这是生命,宇宙和一切的最终问题的答案。(

inject执行循环,其中"累积变量"(也称为"备忘录对象"(是一个两元素阵列,最初设置为[0,1]。在每次迭代中,此对象以[sum, pow]的形式传递给环形主体,以及输入数组中的下一个元素,该元素存储在byte中。循环主体计算出在下一个迭代中使用的更新的备忘录对象。inject的结果是备忘录的最终值。

您可以通过

替换循环主体来遵循正在发生的事情
[pow * byte.ord, pow * 256].tap do
  |new_sum, new_pow|
  puts "Working on byte #{byte.inspect}"
  puts "old sum and pow : #{sum},#{pow}" 
  puts "new sum and pow : #{new_sum}, #{new_pow}"
end

假设字符串为" ab"(ASCII代码为65和66(:

string.reverse.bytes给您[66,65]

[66,65].inject([0,1])通过数组[66,65],并将结果数组[0,1]带入每个迭代中。您的循环需要返回阵列的更改版本,并将其传递给下一个迭代。

示例1:

[66,65].inject([0,1]) do |(sum, pow), byte|
  puts "sum: #{sum} pow: #{pow} byte: #{byte}"
  [sum, pow] # this gets passed to the next round
 end

此输出:

sum: 0 pow: 1 byte: 66
sum: 0 pow: 1 byte: 65

带有不同类型的"备忘录"数组:

memo = []
[66,65].inject(memo) do |memo, byte|
  memo << "byte is #{byte}"
  memo
 end
 puts memo.inspect

此输出:

["byte is 66", "byte is 65"]

所以,注入就像 each,但是给定的"备忘录"对象将从每个回合传递到下一轮。

该方法使用备忘录容纳两个值:下一个字节的总和和乘数。

将调试输出添加到原始方法:

def debase256(string)
  string.reverse.bytes.inject([0, 1]) do |(sum, pow), byte|
    puts "sum: #{sum} pow: #{pow} byte: #{byte}"
    [pow * byte.ord, pow * 256]
  end.first
end

使用debase256("ABC")输出运行此功能:

sum: 0 pow: 1 byte: 67
sum: 67 pow: 256 byte: 66
sum: 16896 pow: 65536 byte: 65

因此,我们看到第一轮|(sum, pow), byte|的输入是(0, 1), 67

pow * byte.ord1 * 67 = 67

pow * 2561 * 256 = 256

所以,第二轮的|(sum, pow), byte|将是:(67, 256), 66

pow * byte.ord256 * 66 = 16896

pow * 256256 * 256 = 65536

所以上一轮的|(sum, pow), byte|是: (16896, 65536), 65

pow * byte.ord65536 * 65 = 4259840

pow * 25665536 * 256 = 16777216

因为这是最后一轮,因此块将返回备忘录,即[4259840, 16777216]。第一个元素包含所需的结果,最后一个元素不再需要,因此.first被要求获得总和。

最新更新