有什么更自然的方法在生锈时这样做吗?我很难把这个弄干净。类型错配和借来让我很困惑。我能做些什么再因素?
我将books_to_files映射缩写为只包含几个不同单词长度的书籍示例。
In: Out:
Genesis 1:1 Genesis, 1:1, mhc1.txt
Song of Solomon 2:2-4 Song of Solomon, 2:2-4, mhc3.txt
Acts of the Apostles 3:3-9 Acts of the Apostles, 3:3-9, mhc6.txtuse std::env;
use std::collections::HashMap;
fn main() {
let args: Vec<String> = env::args().collect();
let book: String;
if args.len() == 3 {
book = args[1].clone();
} else if args.len() == 4 {
book = args[1].clone() + " " + &args[2].clone();
} else if args.len() == 5 {
book = args[1].clone() + " " + &args[2].clone() + " " + &args[3].clone();
} else if args.len() == 6 {
book = args[1].clone() + " " + &args[2].clone() + " " + &args[3].clone() + " " + &args[4].clone();
} else {
panic!("Wrong number of arguments");
}
let reference: String = args[args.len()-1].clone();
let file: String = get_file(&book);
println!("{}, {}, {}", book, reference, file);
}
fn get_file(book: &String) -> String {
let books_to_files: HashMap<&str, &str> = [
("Genesis", "mhc1.txt"),
("First Samuel", "mhc2.txt"),
("Song of Solmon", "mhc3.txt"),
("Acts of the Apostles", "mhc6.txt"),
("First Corinthians", "mhc6.txt"),
("Galatians", "mhc6.txt"),
].iter().cloned().collect();
books_to_files.get::<str>(&book.to_string()).unwrap().to_string()
}
```发布于 2019-07-29 16:19:12
我收集了一些关于可能的重构因素的想法,您可以在下面考虑。
if链不是最有效的方式
正如您所写的,您的代码只适用于长度在1到4个单词之间的标题。这不是一个灾难,它可能会对大多数标题有效,但我们可以重写您的代码,使其更短,并处理任意长度的标题。双赢!
skip()。设args: Vec = env::args().skip(1).collect();if链放在一起。使用范围,我们可以得到与除最后一个元素以外的所有元素对应的args向量的切片。然后,我们可以使用join()将该切片转换为String。例如,如果我们从vec!["Song", "of", "Solomon", "2:2-4"]在args中开始,我们将拿出一个片段来获取["Song", "of", "Solomon"],然后将它们与中间的" "重新连接,以获得"Song of Solomon"。设book = args(Arg)_(第一条).join(“");clone(),除非有必要当我查看您的代码时,我注意到的第一件事是,您已经在很多地方使用了clone()。我们已经用上面的调整处理掉了很多这样的问题,当你不需要分配的时候,你应该尽量避免它们,因为这是浪费时间和记忆。如果您可以使用&str,那么可以这样做,而不是坚持所有字符串都是String。顺便说一句,&String很少有问题--只需在那里使用&str即可。
reference成为一个&str,并避免克隆。让我们参考:&str = &argsargs.len() -1;get_book的S方法签名,如上面建议的. fn get_file(book:&str) -> &str {to_string()了。books_to_files.get::(&book.to_string()).unwrap()main()中调整这一行以接受&str。让档案:&str = get_file(&book);理想情况下,get_book应该返回一个Option,这样调用方就可以选择如何处理错误。因为它是致命的无论如何,展开没有什么区别,但值得记住,作为一个未来的改进。
发布于 2019-07-30 01:07:41
我们可以使用一个匹配块,而不是使用hashmap来查找书籍。(这是假设您的书是在编译时就知道的。)此外,我们还可以返回一个Option<&'static str>。该选项允许函数的调用方决定如何处理失败,而&'static str是字符串文本的类型。
fn get_file(book: &str) -> Option<&'static str> {
match book {
"Genesis" => Some("mhc1.txt"),
"First Samuel" => Some("mhc2.txt"),
"Song of Solmon" => Some("mhc3.txt"),
"Acts of the Apostles" => Some("mhc6.txt"),
"First Corinthians" => Some("mhc6.txt"),
"Galatians" => Some("mhc6.txt"),
_ => None,
}
}根据您的响应,我建议使用第一个命令行参数作为整本书的名称。我会写像这样的main。我们可以使用切片模式来检查参数的正确数量,并同时将它们绑定到变量。然后,我们可以尝试找到文件或打印错误消息,否则。如果参数数是错误的,我们还打印了一个错误,显示程序的正确用法。
fn main() {
let args: Vec<String> = std::env::args().collect();
if let [_, book, reference] = args.as_slice() {
if let Some(file) = get_file(book) {
println!("{}, {}, {}", book, reference, file);
} else {
eprintln!("Could not find book!");
}
} else {
eprintln!("Usage: {} <BOOK> <REFERENCE>", args[0]);
}
}发布于 2019-08-12 13:42:53
以下是一些改进:
skip it env::args().skip(1).collect()pop获取引用(最后一个arg)join 获取这本书(剩余的args)args.join(" ")&str get_file(book: &str)match match book {...}Option/Some/None,以便main可以处理错误get_file(...) -> Option<&str>use std::env;
fn main() {
let mut args: Vec<String> = env::args().skip(1).collect();
let reference = args.pop()
.expect("Expected 2+ parameters: <book of the Bible> <reference>");
let book = args.join(" ");
let file = get_file(&book)
.expect("Not a valid book");
println!("{}, {}, {}", book, reference, file);
}
fn get_file(book: &str) -> Option<&str> {
match book {
"Genesis" => Some("mhc1.txt"),
"First Samuel" => Some("mhc2.txt"),
"Song of Solmon" => Some("mhc3.txt"),
"Acts of the Apostles" => Some("mhc6.txt"),
"First Corinthians" => Some("mhc6.txt"),
"Galatians" => Some("mhc6.txt"),
_ => None
}
}感谢Aurora0001和JayDepp提供的有益反馈(占上述建议的90% )。这段代码现在更自然了。
https://codereview.stackexchange.com/questions/225035
复制相似问题