编写模板
读取一行字符串
在Python中,我们一般使用如下代码来读取输入。
a = input()
带提示的读取
a = input("input sth.")
在rust中,我们应该这样获取输入。
fn main() {
use std::io;
// 导入io操作需要的库
let mut tmp = String::new();
// 先声明并初始化一个名为 tmp 的 可变String
io::stdin().read_line(&mut tmp).unwrap();
// 从stdin读取一行输入并通过可变引用绑定到tmp
// 注意这种方法读取的字符串尾会带上\r\n
println!("{:?}", tmp.chars());
println!("{:?}", tmp.trim().chars());
}
结果为
abc
Chars(['a', 'b', 'c', '\r', '\n'])
Chars(['a', 'b', 'c'])
唔,好啰嗦的感觉。我们甚至没有处理输入错误,而是使用的unwrap
。
好消息,我们可以使用rust中的声明宏实现类似的效果。
在 Rust 中宏分为两大类:声明式宏( declarative macros ) macro_rules! 和三种过程宏( procedural macros )1
我们先定义一个名为input的声明宏。
macro_rules! input {
() => {
};
}
然后把上面读取输入的代码粘贴进去。
macro_rules! input {
() => {
{
let mut tmp = String::new();
io::stdin().read_line(&mut tmp).unwrap();
tmp.trim().to_string()
}
};
}
我们的基础款就完成了。
WARNING我们应该在宏的上面引入
std::io
fn main() {
let a = input!();
println!("{}", a);
}
Hello world!
Hello world!
我觉得这样很酷😎,不觉得吗?
很方便有木有。
但是我们还有一个功能,输入之前提示一下用户。
应该不会有人盯着终端半天等结果但是忘记输入吧😎👌😭。
首先添加另一个分支。它应该接收一个参数作为提示。
大概应该长这样
($prompt:expr) => {
{
}
};
宏里面的参数都应该使用美元符号开头,具体请参看脚注的详细教程。
然后我们先把上面的实现粘贴进去,毕竟我们还是得读取输入的。
添加输出到stdout
并且刷新(不刷新不会实时显示)。
最终结果
use std::io::{ self, Write };
macro_rules! input {
() => {
{
let mut tmp = String::new();
io::stdin().read_line(&mut tmp).unwrap();
tmp.trim().to_string()
}
};
($prompt:expr) => {
{
print!($prompt);
io::stdout().flush().unwrap();
let mut tmp = String::new();
io::stdin().read_line(&mut tmp).unwrap();
tmp.trim().to_string()
}
};
}
使用
fn main() {
let a = input!("input sth:");
println!("{}", a);
}
input sth:sth
sth
读取一行数字
在Python中只需要一行。
ls = list(map(int, input().split()))
print(ls)
1 2 3 4
[1, 2, 3, 4]
可恶,确实好方便。
但是,我们也不是没有办法。
我们的目标是将字符串分割成Vector,然后将字符串转换为具体的类型。
再怎么说,我们也是函数式的。😍
我的回合,抽卡!!!我发动魔术手。
首先定义一个特型。
trait OJ {
fn to_vec<T>(&self, p: &str) -> Result<Vec<T>, ParseIntError>
where T: FromStr<Err = ParseIntError>;
}
为了方便,我们使用的泛型。 只要是能从字符串转为某类型的(实现了FromStr trait的类型),我们就接受。
没啥含金量的实现。
impl OJ for str {
fn to_vec<T>(&self, separator: &str) -> Result<Vec<T>, ParseIntError>
where T: FromStr<Err = ParseIntError>
{
self.split(separator)
.map(|s| s.parse::<T>())
.collect()
}
}
但是,我们的代码里有一个奇怪的东西str
。
???
这是什么情况
没错,宛如神奇的魔术一般,不用像Java一般继承基类,我们向位于标准库中的str类型添加了自己的方法。
因为String底层是str,所以String也有了这个方法。
use std::{ io::{ self, Write }, num::ParseIntError, str::FromStr };
macro_rules! input {
() => {
{
let mut tmp = String::new();
io::stdin().read_line(&mut tmp).unwrap();
tmp.trim().to_string()
}
};
($prompt:expr) => {
{
print!($prompt);
io::stdout().flush().unwrap();
let mut tmp = String::new();
io::stdin().read_line(&mut tmp).unwrap();
tmp.trim().to_string()
}
};
}
trait OJ {
fn to_vec<T>(&self, p: &str) -> Result<Vec<T>, ParseIntError>
where T: FromStr<Err = ParseIntError>;
}
impl OJ for str {
fn to_vec<T>(&self, separator: &str) -> Result<Vec<T>, ParseIntError>
where T: FromStr<Err = ParseIntError>
{
self.split(separator)
.map(|s| s.parse::<T>())
.collect()
}
}
跑一下测试
fn main() {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string() {
assert_eq!(vec![1, 2, 3], String::from("1 2 3").to_vec(" ").unwrap());
}
#[test]
fn test_str() {
assert_eq!(vec![1, 2, 3], "1 2 3".to_vec(" ").unwrap());
}
}
顺利通过测试。到这里,我们的模板基本成型了。
好,就叫他天上天下天地无双
模板
实战
两数之和
如果你没有听过两数之和的鼎鼎大名,那我在这里介绍一下,两数之和犹如英语四级词汇中的abandon一样的地位。
题目链接 洛谷 A+B
// 上面为模板,这里省略,提交的时候应该有。
fn main() {
if let Ok(ls) = input!().to_vec::<i32>(" ") {
let (a, b) = (ls[0], ls[1]);
println!("{}", solve(a, b))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_1() {
assert_eq!(50, solve(20, 30));
}
}
评测结果:
编程语言 | 代码长度 | 用时 | 内存 |
---|---|---|---|
Rust O2 | 1.22KB | 30ms | 576.00KB |
如果您有更好的方案和实现,请告知我。本质是为了抛砖引玉。