let mut sum = 0; for i in 0..10 { sum += i; }即使在这样一个简短的示例中,我们也可以看到我们试图解决的问题与我们编写的代码之间的差异:我们不关心sum的中间值,只关心最后的结果。
let sum: u32 = (0..10).sum();
在像这样的小示例中,这可能无关紧要,但是当我们开始使用嵌套循环时,我们看到在命令式方法中,更多的行专门用于临时记录,这会导致代码的复杂性增加(我们自己引入的不必要的复杂性)。复杂性,无论多小,都会耗费注意力。
let languages = vec![ Language::new("Rust", vec![Paradigm::Functional, Paradigm::ObjectOriented], 100_000), Language::new("Go", vec![Paradigm::ObjectOriented], 200_000), Language::new("Haskell", vec![Paradigm::Functional], 5_000), Language::new("Java", vec![Paradigm::ObjectOriented], 1_000_000), Language::new("C++", vec![Paradigm::ObjectOriented], 1_000_000), Language::new("Python", vec![Paradigm::ObjectOriented, Paradigm::Functional], 1_000_000), ];下面是一个使用嵌套for循环的解决方案:
// 过滤语言,只保留支持函数式编程的语言 let mut functional_languages = vec![]; for language in languages { if language.paradigms.contains(&Paradigm::Functional) { functional_languages.push(language); } } // 按用户数量降序对函数式语言进行排序 for i in 1..functional_languages.len() { let mut j = i; while j > 0 && functional_languages[j].users > functional_languages[j - 1].users { functional_languages.swap(j, j - 1); j -= 1; } } // 堆代码 duidaima.com // 只保留前5种编程语言 while functional_languages.len() > 5 { functional_languages.pop(); }这是一个非常冗长、命令式的解决方案。我们就地改变矢量,并在此过程中破坏中间结果。虽然这是正确的,但我认为它不是最地道的Rust代码。
let mut top_languages = vec![]; for language in languages { if language.paradigms.contains(&Paradigm::Functional) { top_languages.push(language); } } // 把我们的语言按流行程度降序排序 // 这一行在本质上已经有点函数式了 top_languages.sort_by_key(|lang| std::cmp::Reverse(lang.users)); top_languages.truncate(5);我们不妨在过滤时更简洁一点:
let top_languages: Vec<Language> = languages .iter() // 只保留函数式语言 .filter(|language| language.paradigms.contains(&Paradigm::Functional)) // Sort our languages in descending order of popularity. .sorted_by_key(|lang| Reverse(lang.users)) // 只保留前5种语言 .take(5) // 将结果收集到一个新的向量中 .collect();对整个列表进行排序(即使经过过滤),只提取前5个元素似乎有些低效。这突出了Rust与C++相比的一个限制,C++在其标准库中提供了一个partial_sort函数。虽然Rust在std中没有等价的,但有第三方的crate。或者,也可以使用BinaryHeap。