Notes on codes, projects and everything

Learning a new language for more than 2 months (feat. Exercism)

Luhn

Then there’s this checksum validator. Starting from the right, just double every digit in the odd index, minus 9 from it if it exceeds 9, then sum up the resulting digit, as well as the even-indexed digits. The result of the sum has to be 10-divisible.

pub fn is_valid(code: &str) -> bool {
    let _code = code.chars().filter(|x| !x.is_whitespace());

    match _code.clone().count() {
        _invalid if _invalid <= 1 => false,
        _ => {
            let result = _code
                .map(|x| x.to_digit(10))
                .rev()
                .enumerate()
                .map(|(idx, x)| match x {
                    Some(n) => Some(match (idx % 2 == 1, n * 2) {
                        (true, result) if result > 9 => result - 9,
                        (true, result) => result,
                        _ => n,
                    }),
                    None => None,
                })
                .fold(Some(0), |current, incoming| {
                    current.and_then(|x| match incoming {
                        Some(n) => Some(x + n),
                        None => None,
                    })
                });

            match result {
                Some(n) if n % 10 == 0 => true,
                _ => false,
            }
        }
    }
}

This is one of the earlier submissions, and somehow I still struggle with ownership here. Then I started getting intrigue with the power of the Option enum. Then I had this

pub fn is_valid(code: &str) -> bool {
    let _code = code
        .chars()
        .filter(|x| !x.is_whitespace())
        .collect::<Vec<char>>();

    match _code.len() {
        _invalid if _invalid <= 1 => false,
        _ => _code
            .iter()
            .map(|x| x.to_digit(10))
            .rev()
            .enumerate()
            .map(|(idx, x)| {
                x.and_then(move |n| {
                    Some(match (idx % 2 == 1, n * 2) {
                        (true, result) if result > 9 => result - 9,
                        (true, result) => result,
                        _ => n,
                    })
                })
            }).fold(Some(0), |current, incoming| {
                current.and_then(|x| match incoming {
                    Some(n) => Some(x + n),
                    None => None,
                })
            }).map_or(false, |n| n % 10 == 0),
    }
}

Atbash Cipher

I like breaking things, though I hate the frustration when things don’t work as expected. This time I am trying to send in a closure into a function as parameter. Of course this is not necessary the best solution but getting to this state took me quite some time due to the fight with the borrow checker.

pub fn encode(plain: &str) -> String {
    convert(
        plain,
        Some(&|idx, x| format!("{}{}", x, if (idx + 1) % 5 == 0 { " " } else { "" })),
    )
}

/// "Decipher" with the Atbash cipher.
pub fn decode(cipher: &str) -> String {
    convert(cipher, None::<&Fn(usize, char) -> String>)
}

fn transform(x: char) -> char {
    match x {
        x if x.is_alphabetic() => (219u8 - x as u8) as char,
        x => x,
    }
}

fn preprocess(text: &str) -> Vec<char> {
    text.to_ascii_lowercase()
        .chars()
        .filter(|x| x.is_alphanumeric() && x.is_ascii())
        .collect()
}

fn convert(text: &str, decorator: Option<&Fn(usize, char) -> String>) -> String {
    (match decorator {
        Some(_decorator) => preprocess(text)
            .into_iter()
            .enumerate()
            .map(|(idx, x)| _decorator(idx, transform(x)))
            .collect::<String>(),
        None => preprocess(text)
            .into_iter()
            .map(|x| transform(x))
            .collect::<String>(),
    }).trim()
        .to_string()
}

Sometimes having someone experienced as a mentor is a good thing, he immediately pointed out some parts that can made the code simpler, yielding this

/// "Encipher" with the Atbash cipher.
pub fn encode(plain: &str) -> String {
    convert(
        plain,
        Some(&|idx, x| format!("{}{}", if idx != 0 && idx % 5 == 0 { " " } else { "" }, x)),
    )
}

/// "Decipher" with the Atbash cipher.
pub fn decode(cipher: &str) -> String {
    convert(cipher, None::<&Fn(usize, char) -> String>)
}

fn transform(x: char) -> char {
    match x {
        x if x.is_alphabetic() => (219u8 - x as u8) as char,
        x => x,
    }
}

fn convert(text: &str, decorator: Option<&Fn(usize, char) -> String>) -> String {
    let preprocess = || {
        text.chars()
            .flat_map(char::to_lowercase)
            .filter(|x| x.is_alphanumeric() && x.is_ascii())
    };

    (match decorator {
        Some(_decorator) => preprocess()
            .enumerate()
            .map(|(idx, x)| _decorator(idx, transform(x)))
            .collect::<String>(),
        None => preprocess().map(|x| transform(x)).collect::<String>(),
    })
}

The idea is more or less the same compared to the earlier iteration shown above, just slightly cleaner.

leave your comment

name is required

email is required

have a blog?

This blog uses scripts to assist and automate comment moderation, and the author of this blog post does not hold responsibility in the content of posted comments. Please note that activities such as flaming, ungrounded accusations as well as spamming will not be entertained.

Pings

Click to change color scheme