Skip to content

Instantly share code, notes, and snippets.

@RanolP
Last active July 10, 2020 04:33
Show Gist options
  • Save RanolP/3d17fdb99fbf30d6c67bfac1cc366133 to your computer and use it in GitHub Desktop.
Save RanolP/3d17fdb99fbf30d6c67bfac1cc366133 to your computer and use it in GitHub Desktop.
Easier input processing in Rust when Problem Solving
//! This is free and unencumbered software released into the public domain.
//!
//! Anyone is free to copy, modify, publish, use, compile, sell, or
//! distribute this software, either in source code form or as a compiled
//! binary, for any purpose, commercial or non-commercial, and by any
//! means.
//!
//! In jurisdictions that recognize copyright laws, the author or authors
//! of this software dedicate any and all copyright interest in the
//! software to the public domain. We make this dedication for the benefit
//! of the public at large and to the detriment of our heirs and
//! successors. We intend this dedication to be an overt act of
//! relinquishment in perpetuity of all present and future rights to this
//! software under copyright law.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//! IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
//! OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//! ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//! OTHER DEALINGS IN THE SOFTWARE.
//!
//! For more information, please refer to <http://unlicense.org/>
//!
//! Source Code URL:
//! https://gist.github.com/RanolP/3d17fdb99fbf30d6c67bfac1cc366133
use std::io::{stdin, BufRead, ErrorKind, StdinLock};
fn get_word(stdin: &mut StdinLock) -> String {
let mut buffer = Vec::new();
loop {
let available = match stdin.fill_buf() {
Ok(n) => n.to_vec(),
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
_ => panic!(),
};
if available.len() == 0 && buffer.len() != 0 {
break String::from_utf8(buffer).unwrap().trim().to_string();
}
let count = available
.iter()
.enumerate()
.find(|(_, chr)| match chr {
b' ' | b'\n' | b'\r' => true,
_ => false,
})
.map(|(idx, _)| idx);
if let Some(count) = count {
buffer.extend_from_slice(&available[..count]);
stdin.consume(count + 1);
if !buffer.is_empty() {
break String::from_utf8(buffer).unwrap().trim().to_string();
}
} else {
buffer.extend_from_slice(&available);
stdin.consume(available.len());
}
}
}
macro_rules! input {
(@inner $stdin:ident; line) => {{
let mut buf = String::new();
$stdin.read_line(&mut buf).unwrap();
buf.trim_end_matches('\n').to_string()
}};
(@inner $stdin:ident; word) => {
get_word(&mut $stdin)
};
(@inner $stdin:ident; ($($t:ty),*)) => {
($(input!(@inner $stdin; $t),)*)
};
(@inner $stdin:ident; [$size:literal; ($($t:ty),*)]) => {{
let mut result = [<($($t),*) as Default>::default(); $size];
for i in 0..$size {
result[i] = ($(input!(@inner $stdin; $t)),*);
}
result
}};
(@inner $stdin:ident; [$size:literal; $t:ty]) => {
input!(@inner $stdin; [$size; ($t)])
};
(@inner $stdin:ident; Vec[$size:expr; $($t:tt)+]) => {{
let mut result = Vec::with_capacity($size);
for _ in 0..$size {
result.push(input!(@inner $stdin; $($t)*));
}
result
}};
(@inner $stdin:ident; HashSet[$size:expr; $($t:tt),+]) => {{
let mut result = std::collections::HashSet::with_capacity($size);
for _ in 0..$size {
result.insert(input!(@inner $stdin; $($t)*));
}
result
}};
(@inner $stdin:ident; BTreeSet[$size:expr; $($t:tt),+]) => {{
let mut result = std::collections::BTreeSet::new();
for _ in 0..$size {
result.insert(input!(@inner $stdin; $($t)*));
}
result
}};
(@inner $stdin:ident; $t:ty) => {
input!(@inner $stdin; word).parse::<$t>().unwrap()
};
($size:literal; ($($t:ty),*)) => {
input!([$size; ($($t),*)])
};
($size:literal; $t:ty) => {
input!([$size; $t])
};
($($t:tt)+) => {{
let stdin = stdin();
let mut stdin = stdin.lock();
input!(@inner stdin; $($t)+)
}};
}
fn main() {
println!("{:?}", input!(line));
println!("{:?}", input!(word));
println!("{:?}", input!((i32, f32)));
println!("{:?}", input!([2; (i32, f32)]));
println!("{:?}", input!([4; i32]));
println!("{:?}", input![2; (i32, f32)]);
println!("{:?}", input![4; i32]);
let size = input!(usize);
println!("{:?}", input!(Vec[size; i32]));
println!("{:?}", input!(HashSet[size; i32]));
println!("{:?}", input!(BTreeSet[size; i32]));
println!("{:?}", input!(Vec[size; HashSet[2; i32]]));
}
@RanolP
Copy link
Author

RanolP commented Jan 19, 2020

Rust PS Input

A little utility that makes you comfortable when Problem Solving.

Guide

Easy. Just write like below code to fit your requirements.

let (vars, ...) = input!(what_to_insert);
  • Use line to accept a single line.
  • Use word to accept a single word.
  • Use F: FromStr to accept a parsed data from a single word.
  • Use [n; T] where T: FromStr to accept an n-sized (n must be a literal) array from the word. Sorry for the mismatch with the Rust's syntax.
  • Use Vec[n; T] to accept an n-sized vector from the word.
  • Use HashSet[n; T] to accept an n-sized HashSet from the word.
  • Use (A, B, ...) where A: FromStr, B: FromStr, ... to accept tuple.

Breaking Changes

  • I used [T; n] syntax on n-sized collection for a short time, but because of the Vec and HashSet processor. I changed syntax.

Specials

  • You can use input![n; T] to accept an n-sized array. Just an alias.

Inside the Macro

Preprocess

From line 108.
Create a stdin variable and use input! macro to loop every element.

The line Processor

From line 64 to 68. I just used BufRead.read_line to get a line and remove trailing \n character.

The word Processor

From line 70.
Just calling the get_word function.
I can't use BufRead.read_until because I want to match any of the space characters: ' ', '\r', '\n'.
With the effort of @kiwiyou,
I finally made those codes that give us the first non-empty word.
And sometimes the input does not end with whitespace characters.
Line 39 to 41 handles that gently.

The Tuple Processor

From line 72.
I've implemented the tuple processor.

The [n; T] Processor

From line 75 to 84 and 102 to 107.
[n; T] processor separated into two rules: tuple array and type array.
$t:ty can't be treated as separated tokens, so it would be passed into the rule (@inner $stdin:ident; $t:ty).
So I've created a new rule (@inner $stdin:ident; [$size:literal; ($($t:ty),*)]).

And I wish to use input![n; T] syntax.
So I've added ($size:literal; ($($t:ty),*)) and ($size:literal; $t:ty) rules.

The Vec[n; T] Processor

From line 85 to 91.
The processor added because array can't be initialized with runtime-known size value.

The HashSet[n; T] Processor

From line 93 to 98.
The processor added because sometimes we know that the input gives unique values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment