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.