Алфавит Сайфер в Русте


Это моя попытка алфавит Сайфер в Русте. Я уверен, если итерации и толкает к изменчивому это лучший способ, чтобы отрегулировать здание выходной; кажется хорошим кандидатом на карте, но я не могу выяснить, как это сделать. Может кто-нибудь помочь?

use std::env;

fn main() {
    let keyword = env::args().nth(1).unwrap();
    let body = env::args().nth(2).unwrap();
    let transpose_table = get_transpose_table(keyword);

    let mut output = String::new();
    for (input_idx, input_char) in body.char_indices() {
        let transpose_distance = transpose_table[input_idx % transpose_table.len()];
        let transposed_char_idx = char_to_alpha_idx(input_char) + transpose_distance;
        output.push(alpha_idx_to_char(transposed_char_idx % 26));
    }

    println!("{}", output);
}

fn get_transpose_table(keyword: String) -> Vec<usize> {
    return keyword.chars()
                  .map(|character| char_to_alpha_idx(character))
                  .collect();
}

fn char_to_alpha_idx(character: char) -> usize {
    return "abcdefghijklmnopqrstuvwxyz".find(character)
                                       .expect("Bad char for char_to_alpha_idx");
}

fn alpha_idx_to_char(loc: usize) -> char {
    return "abcdefghijklmnopqrstuvwxyz".chars()
                                       .nth(loc)
                                       .expect("Bad loc for alpha_idx_to_char");
}


134
5
задан 12 апреля 2018 в 02:04 Источник Поделиться
Комментарии
1 ответ

Отказ от ответственности: я новичок ржавчины с опытом работы в C и C++. Теперь, когда я потерял мое доверие, давайте посмотрим на ваш код.

Использовать rustfmt, скрепка и rustc

Есть (как минимум) три полезные инструменты, когда один пишет Руст код: rustfmt, clippy и компилятора rustc себя.

Rustfmt обеспечивает общую основу

Компилятор не дает никаких предупреждений, но rustfmt будет изменить свой код.
Столько ржавчины разработчики используют rustfmt (или cargo fmt, которая призывает бывших), вы также должны сделать привычку использовать его. Таким образом, другие будут чувствовать себя как дома, если они смотрят на ваш код, и то же самое будет для вас, когда вы смотрите на них.

Предупреждения пока это

clippy С другой стороны будет ловить распространенных ошибок. В этом случае ее, как правило, избыточные returns в idx функции:

fn get_transpose_table(keyword: String) -> Vec<usize> {
keyword
.chars()
.map(|character| char_to_alpha_idx(character))
.collect()
}

fn char_to_alpha_idx(character: char) -> usize {
"abcdefghijklmnopqrstuvwxyz"
.find(character)
.expect("Bad char for char_to_alpha_idx")
}

fn alpha_idx_to_char(loc: usize) -> char {
"abcdefghijklmnopqrstuvwxyz"
.chars()
.nth(loc)
.expect("Bad loc for alpha_idx_to_char")
}

Нет необходимости return здесь. Далее, вы никогда не потребляете String в get_transpose_table, чтобы мы могли взять его с &String. Но, в конце концов, мы никогда не используем каких-либо String-специфические функции, поэтому мы можем просто использовать &str.

Пока мы здесь, map(|x| func(x)) просто map(func)так Давайте изменим это как-ну:

fn get_transpose_table(keyword: &str) -> Vec<usize> {
keyword.chars().map(char_to_alpha_idx).collect()
}

Все эти небольшие улучшения, сообщили clippy.

Использовать T::with_capacity если вы знаете, сколько элементов вы будете иметь

Для основного алгоритма, вы должны использовать String::with_capacity если вы знаете количество символов, которые вы хотите сохранить:

let mut output = String::with_capacity(input.len());

Ничего другого в ваши изменения кода.

Закрытие и читабельность

Помимо лучшего названия (message вместо body, body_index или message_ index вместо input_index) это все, что мы можем сделать с этим алгоритмом. Мы могли бы превратить его в замыкание:

let output: String = message
.char_indices()
.map(|(index, message_char)| {
let transpose_distance = transpose_table[index % transpose_table.len()];
let transposed_char_idx = char_to_alpha_idx(message_char) + transpose_distance;
alpha_idx_to_char(transposed_char_idx % 26)
})
.collect();

но мы только сняли переменчивости. Так давайте вернемся к таблице рисунок. Зачем нам нужен index? Потому что мы хотим знать, что usize мы должны использовать от transpose_table. Мы хотели бы пройти цикл этих ценностей, в то же время оригинального сообщения.

Это именно то, что cycle() и zip() предназначены для:

let output: String = transpose_table
.iter()
.cycle()
.zip(message.chars())
.map(|(transpose_distance, message_char)| {
let transposed_char_idx = char_to_alpha_idx(message_char) + transpose_distance;
alpha_idx_to_char(transposed_char_idx % 26)
})
.collect();

Будет ли это легче понять, чем ваш первоначальный вариант оставили для вас. Однако, мы можем также избавиться от промежуточных Vec:

let output: String = keyword
.chars()
.map(char_to_alpha_idx)
.cycle()
.zip(message.chars())
.map(|(transpose_distance, message_char)| {
let transposed_char_idx = char_to_alpha_idx(message_char) + transpose_distance;
alpha_idx_to_char(transposed_char_idx % 26)
})
.collect();

Это всего лишь горстка, но это работает точно так же, как в предыдущем варианте.

Инкапсулировать функциональность в функции

Теперь моя главная критика заключается в том, что не предоставляем единого функция для вашего шифра. Трудно проверить свои возможности на данный момент, так что нам лучше добавить одну и некоторую документацию:

/// Applies [The Alphabet Cipher] given by `keyword` to `message`.
///
/// Both `message` and `keyword` must only contain lowercase alphabetic ASCII
/// characters.
///
/// # Panics
/// Panics if any non-lowercase or non-alphabetic ASCII character is encountered.
///
/// # Examples
/// The character 'a' in the keyword won't change the input:
/// ```
/// assert_eq!(alphabet_cipher("aaaaa", "hello"), "hello");
/// ```
///
/// They keyword gets cycled if its shorter than the message:
/// ```
/// assert_eq!(alphabet_cipher("a", "hello"), "hello");
/// assert_eq!(alphabet_cipher("b", "hello"), "ifmmp");
/// assert_eq!(alphabet_cipher("ab","hello"), "hflmo");
/// assert_eq!(alphabet_cipher("ba","hello"), "iemlp");
/// ```
///
/// [The Alphabet Cipher]: https://en.wikipedia.org/wiki/The_Alphabet_Cipher
fn alphabet_cipher(keyword: &str, message: &str) -> String {
keyword
.chars()
.map(char_to_alpha_idx)
.cycle()
.zip(message.chars())
.map(|(offset, message_char)| {
let index = char_to_alpha_idx(message_char) + offset;
alpha_idx_to_char(index % 26)
})
.collect()
}

Фрагменты кода в комментарии также проходят проверку cargo testно мы можем также добавить некоторые дополнительные тесты, чтобы проверить, что наши функции работают, например:

#[test]
fn alphabet_cipher_rot_a_to_z() {
assert_eq!(
alphabet_cipher("abcdefghijklmnopqrstuvwxyz", "aaaaaaaaaaaaaaaaaaaaaaaaaa"),
"abcdefghijklmnopqrstuvwxyz"
);
}

Есть еще тесты можно придумать, но это осталось в качестве упражнения.

Дружественный пользовательский интерфейс

Вы используете unwrap() довольно много. Тем не менее, что оставляет пользователей с бесполезными сообщениями об ошибках:

$ ./cypher test
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', ...

$ ./cypher test test12345
thread 'main' panicked at 'Bad char for char_to_alpha_idx', ...

Эти сообщения об ошибках не очень полезно. Они хороши для игрушечных программ, но вы должны посмотреть unwrap "я знаю, что это будет Ok или Someи это хорошо для паники, если это не так, потому что я не знаю, как продолжить". Тем не менее, даже в этих случаях конечный пользователь должен знать, как продолжить. Имейте это в виду при написании больших программ.

7
ответ дан 12 апреля 2018 в 05:04 Источник Поделиться