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号。我们通过将每个插槽索引i
与256^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的奇特词。通常,十六进制的数字表示为0
至9
,而a
至f
:
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 + 160
是172
,它是颜色中红色组件的值。
再次,它与其他示例中的逻辑相同。
我希望这些示例可以帮助您了解这是如何工作的。作为练习,请尝试将此二进制(即基数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.ord
是1 * 67 = 67
pow * 256
是1 * 256 = 256
。
所以,第二轮的|(sum, pow), byte|
将是:(67, 256), 66
。
pow * byte.ord
是256 * 66 = 16896
。
pow * 256
是256 * 256 = 65536
。
所以上一轮的|(sum, pow), byte|
是: (16896, 65536), 65
:
pow * byte.ord
是65536 * 65 = 4259840
。
pow * 256
是65536 * 256 = 16777216
。
因为这是最后一轮,因此块将返回备忘录,即[4259840, 16777216]
。第一个元素包含所需的结果,最后一个元素不再需要,因此.first
被要求获得总和。