简单密码 - Ruby 来自 Exercism

Simple Cipher - Ruby from Exercism

我目前正在学习 Ruby 并尝试过简单密码挑战。我现在正在研究以下解决方案,并试图通过逆向工程来了解该解决方案背后的思考过程。以下是link的解决方法。我将详细说明我对每个代码片段的理解。如果他们不对,你能纠正我吗?谢谢! https://exercism.io/tracks/ruby/exercises/simple-cipher/solutions/b200c3d9f10e497bbe2ca0d826df2661

class Cipher
  ALPHABET = [*'a'..'z']

  attr_reader :key

  def initialize(key = nil)
    @key = key || 100.times.map { ALPHABET.sample }.join
    fail ArgumentError, 'invalid chars in key' unless valid?(@key)
  end

  def encode(text)
    a = 'a'.ord
    text.chars.zip(@key.chars).map do |char, key|
      ALPHABET[(char.ord - a + key.ord - a) % ALPHABET.length]
    end.join
  end

  def decode(code)
    code.chars.zip(@key.chars).map do |char, key|
      ALPHABET[char.ord - key.ord]
    end.join
  end

private

  def valid?(key)
    !key.empty? && key !~ /[^a-z]/
  end

end

第 1 部分

@key = key 是否匹配。 ALPHABET 是随机的(使用 .sample 方法)并加入。然后将随机连接的字母迭代 100 次,然后 return 编辑一个新数组(使用 map 方法)。

为了处理无效键错误,使用了 fail ArgumentError。

  def initialize(key = nil)
    @key = key || 100.times.map { ALPHABET.sample }.join
    fail ArgumentError, 'invalid chars in key' unless valid?(@key)
  end

第 2 部分 我了解到这段代码的目的是将明文转换为加密文本。但是,我正在努力处理以下代码的某些部分。

def encode(text)
    a = 'a'.ord
    text.chars.zip(@key.chars).map do |char, key|
      ALPHABET[(char.ord - a + key.ord - a) % ALPHABET.length]
    end.join
  end
  1. .ord 方法 - 将字符转换为其 ASCII 值。一个 = 'a'.ord 。 为什么选择这个角色?不是 z 或其他字符?
  2. .chars 方法 - 使用 .chars 方法将文本字符串分成每个单独的字符。 .chars 比 .split 更有效,因为它将底层字节解析为 return 字符串的字符(我已经阅读了 .chars 与 .split stackover 流上的差异)。
  3. .zip 方法 - 该方法用于比较原始明文字符和加密字符。
  4. .map 方法 - 使用块和 returns 加密文本调用此方法。
  5. .map 方法块 中,有两个参数:char 和key。 char 表示来自原始纯文本的字符,key 表示加密的密钥字符。

  6. 我无法理解这 ALPHABET[(char.ord - a + key.ord - a) % ALPHABET.length] 部分。 ALPHABET 原始纯文本和加密密钥文本字符使用 .ord 方法转换为 ASCII 值。为什么要从这些值中减去一个值?为什么要使用 % 运算符和 ALPHABET.length?

  7. .join 方法 - 我猜.join 用于加入转换后的加密字符。我的理解正确吗?

第 3 部分 在这部分,解码方法,代码被使用。代码是两方(发送方和接收方)之间共享的密钥。但为什么 ALPHABET[char.ord - key.ord]?字符 ascii - 密钥 ascii 值将提供解密的纯文本。我不明白它是如何工作的?

def decode(code)
    code.chars.zip(@key.chars).map do |char, key|
      ALPHABET[char.ord - key.ord]
    end.join
end

第 4 部分 私有方法用于将这段代码与其他代码分开类。有效的方法是验证密钥。有两个条件:key不能为空或者必须是小写字母。

private

  def valid?(key)
    !key.empty? && key !~ /[^a-z]/
  end

@key = key is if key is matched. ALPHABET is randomized (with .sample method) and joined. Randomised joined alphabets are then iterated 100 times and a new array is returned (with map method).

@key = key 如果 key 不是假的(nilfalse);否则,迭代 100 次并从 ALPHABET 中收集一个随机字符;然后将生成的 100 元素数组连接成一个字符串,并将 字符串 分配给 @key没有返回任何内容

ord method - convert a character to its ASCII value. a = 'a'.ord . Why is this character selected? Not z or other characters?

因为 "a" 以 Unicode 开始字母序列(也在 ASCII 中,但 .ord 在这种情况下在 Unicode 上运行),值为 97。但我们感兴趣的是字符在字母表中的位置,而不是 Unicode 中的位置。可以这样想:它是 20:37,而您正在听歌剧。演出于 19:00 开始。你听了多久了?您减去 20:37 - 19:00,得到答案(1 小时 37 分钟)。同样,要知道 'c' 是 #2 字符(在英语中我们说第 3 个字符,但是 Ruby 从 0 开始计数,所以 'a' 是 #0,'c'是#2),你从'c'的位置减去'a'的位置:99 - 97。您不会从 20:37 中减去 23:00 或任何其他时间,因为这对于找出您听了多长时间没有任何意义;我们使用 'a'.ord 而不是 'z' 或其他字符的原因相同。

.zip method - this method is used to compare original plain text characters and encryption characters.

不,它只是通过将 text.chars 中的每个元素与 @key.chars 中的相应元素配对来创建一个新数组。没有进行比较。

I have a trouble understanding this ALPHABET[(char.ord - a + key.ord - a) % ALPHABET.length] part. ALPHABET original plain text and encryption key text characters are converted into ASCII values with .ord method. Why a value is subtracted from those values? Why use % operator and ALPHABET.length?

请参阅上文了解为什么 a

此密码的工作原理是将每个字符偏移对应于密钥中相应字符的字母表位置的空格数。 % 会将事情环绕起来,以便结果始终是 ALPHABET 的有效索引。例如,如果 char.ord - a + key.ord - a 恰好超过了 ALPHABET 的长度,那么它会环绕到开头。例如,如果在上面的计算中得到 29,通常 ALPHABET[29] 会得到 nil,因为没有字母 #29;但是 29 % 263,并且 ALPHABET[3] 是有效的。

Part 3 In this part, decode method, code is used. Code is the secret key shared between two parties (sender and receiver). But why ALPHABET[char.ord - key.ord]? Characters ascii - key ascii value will provide the decrypted plain text. I do not understand how it works?

移位的可以不移位;这就是解码的样子。之前我们将两个字符的位置相加 — (char.ord - a) + (key.ord - a) — 现在我们将它们相减:(char.ord - a) - (key.ord - a)。做一点数学,你会发现两个 a 在这个减法中会相互抵消,所以 (char.ord - a) - (key.ord - a) 等价于 char.ord - key.ord.

之前,我们担心添加的内容可能会超过字母表的大小。在这里,人们可能会担心减法可能会变成负数。但是 Ruby 现在支持我们了:Ruby 中的 ALPHABET[-3] 表示“倒数第 3 个元素”,因此等同于 ALPHABET[23];不需要 %.