Простой шифр - Ruby от Exercism

В настоящее время я изучаю Ruby и попытался выполнить задачу Simple Cipher. Сейчас я изучаю следующее решение и пытаюсь реконструировать, чтобы понять мыслительный процесс, лежащий в основе этого решения. Ниже приведена ссылка на решение. Я подробно расскажу о своем понимании каждого фрагмента кода. Если они не правы, не могли бы вы меня поправить? Спасибо! 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 = ключ - если ключ совпадает. ALPHABET рандомизируется (с помощью метода .sample) и объединяется. Затем рандомизированные объединенные алфавиты повторяются 100 раз, и возвращается новый массив (с методом карты).

Для обработки ошибки недопустимого ключа используется ошибка 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 = 'a'.ord. Почему выбран этот персонаж? Не z или другие символы?
  2. Метод .chars - текстовая строка разделяется на каждый отдельный символ с помощью метода .chars. .chars более эффективен, чем .split, поскольку он анализирует базовые байты для возврата символов строки (я читал разницу между .chars и .split в потоке наложения).
  3. Метод .zip - этот метод используется для сравнения исходных текстовых символов и символов шифрования.
  4. Метод .map - этот метод вызывается с блоком и возвращает зашифрованный текст.
  5. Внутри блока метода .map есть два аргумента: char и key. char представляет собой символ из исходного обычного текста, а ключ представляет собой зашифрованный ключевой символ.

  6. У меня проблемы с пониманием этой ALPHABET[(char.ord - a + key.ord - a) % ALPHABET.length] части. Исходный обычный текст ALPHABET и текстовые символы ключа шифрования преобразуются в значения ASCII с помощью метода .ord. Почему значение вычитается из этих значений? Зачем использовать оператор% и 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 частного метода используется для отделения этого фрагмента кода от других классов. Действительный метод - проверить ключ. Есть два условия: ключ не должен быть пустым или должен состоять из строчных букв.

private

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

person Community    schedule 18.11.2019    source источник


Ответы (1)


@key = ключ - если ключ совпадает. ALPHABET рандомизируется (с помощью метода .sample) и объединяется. Затем рандомизированные объединенные алфавиты повторяются 100 раз, и возвращается новый массив (с методом карты).

@key = key, если key не является ложным (nil или false); в противном случае повторите 100 раз и выберите случайный символ из ALPHABET; затем объедините полученный массив из 100 элементов в строку и присвойте строке значение @key. Ничего не возвращается .

Метод ord - преобразовать символ в его значение ASCII. a = 'a'.ord. Почему выбран этот персонаж? Не z или другие персонажи?

Поскольку "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), вы вычитаете позицию 'a' из позиции 'c': 99 - 97. Вы не стали бы вычитать 23:00 или любое другое время из 20:37, потому что это не имеет никакого смысла в выяснении того, как долго вы слушали; по той же причине, по которой мы используем 'a'.ord, а не 'z' или другой символ.

.zip метод - этот метод используется для сравнения исходных текстовых символов и символов шифрования.

Нет, он просто создает новый массив, объединяя каждый элемент из text.chars в пары с соответствующим элементом из @key.chars. Никакого сравнения не происходит.

У меня проблемы с пониманием этой части ALPHABET [(char.ord - a + key.ord - a)% ALPHABET.length]. Исходный обычный текст ALPHABET и текстовые символы ключа шифрования преобразуются в значения ASCII с помощью метода .ord. Почему значение вычитается из этих значений? Зачем использовать оператор% и ALPHABET.length?

См. Выше, почему a.

Этот шифр работает путем смещения каждого символа на количество пробелов, соответствующих алфавитной позиции соответствующего символа в ключе. % будет обертывать вещи так, чтобы результатом всегда был действительный индекс ALPHABET. Например, если char.ord - a + key.ord - a превысит длину ALPHABET, тогда он перейдет в начало. Например, если вы получите 29 в приведенном выше вычислении, вы обычно получите nil для ALPHABET[29], поскольку нет буквы № 29; но 29 % 26 это 3, а ALPHABET[3] верно.

Часть 3 В этой части используется метод декодирования, код. Код - это секретный ключ, совместно используемый двумя сторонами (отправителем и получателем). Но почему ALPHABET [char.ord - key.ord]? Символы ascii - ключевое значение ascii предоставит расшифрованный простой текст. Я не понимаю, как это работает?

То, что сдвинуто, можно не сдвинуть; вот как выглядит расшифровка. Если раньше мы складывали позиции двух символов - (char.ord - a) + (key.ord - a) - теперь мы их вычитаем: (char.ord - a) - (key.ord - a). Подсчитайте немного, и вы увидите, что два a компенсируют друг друга в этом вычитании, поэтому (char.ord - a) - (key.ord - a) эквивалентно char.ord - key.ord.

Раньше у нас были опасения, что добавление может превысить размер алфавита. Здесь можно опасаться, что вычитание может стать отрицательным. Но теперь Ruby нас поддерживает: ALPHABET[-3] в Ruby означает «3-й элемент с конца», и, таким образом, эквивалентен ALPHABET[23]; нет необходимости в %.

person Amadan    schedule 18.11.2019
comment
БОЛЬШОЕ СПАСИБО @Amadan за подробное объяснение. Они помогают мне лучше понять логику. Я очень ценю вашу помощь!!! - person ; 18.11.2019
comment
@FuriousCodeNinja затем отметьте ответ как правильный зеленым цветом прямо под оценкой ответа. Вы обязаны отметить ответы, которые решили проблему, здесь, на SO. - person Aleksei Matiushkin; 18.11.2019
comment
@AlekseiMatiushkin Спасибо, что дали знать! Я отметил этот ответ зеленым. Ваше здоровье - person ; 18.11.2019