LearningOS/rust-based-os-comp2023
Rustlings Part19: — iterators in Rust
You can do it
| Exercise | Book Chapter |
|---|---|
| variables | §3.1 |
| functions | §3.3 |
| if | §3.5 |
| primitive_types | §3.2, §4.3 |
| vecs | §8.1 |
| move_semantics | §4.1-2 |
| structs | §5.1, §5.3 |
| enums | §6, §18.3 |
| strings | §8.2 |
| modules | §7 |
| hashmaps | §8.3 |
| options | §10.1 |
| error_handling | §9 |
| generics | §10 |
| traits | §10.2 |
| tests | §11.1 |
| lifetimes | §10.3 |
| iterators | §13.2-4 |
| threads | §16.1-3 |
| smart_pointers | §15, §16.3 |
| macros | §19.6 |
| clippy | §21.4 |
| conversions | n/a |
Iterators
This section will teach you about Iterators.
Further information
iterators2.rs
// iterators2.rs
//
// In this exercise, you'll learn some of the unique advantages that iterators
// can offer. Follow the steps to complete the exercise.
//
// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a
// hint.
// I AM NOT DONE
// Step 1.
// Complete the `capitalize_first` function.
// "hello" -> "Hello"
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
Some(first) => ???,
}
}
// Step 2.
// Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
vec![]
}
// Step 3.
// Apply the `capitalize_first` function again to a slice of string slices.
// Return a single string.
// ["hello", " ", "world"] -> "Hello World"
pub fn capitalize_words_string(words: &[&str]) -> String {
String::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(capitalize_first("hello"), "Hello");
}
#[test]
fn test_empty() {
assert_eq!(capitalize_first(""), "");
}
#[test]
fn test_iterate_string_vec() {
let words = vec!["hello", "world"];
assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]);
}
#[test]
fn test_iterate_into_string() {
let words = vec!["hello", " ", "world"];
assert_eq!(capitalize_words_string(&words), "Hello World");
}
}
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().to_string() + c.as_str(),
}
} 解释含义
这是一个Rust编程语言中的函数,名为`capitalize_first`,它的目的是将输入字符串的第一个字符转换为大写字毅,然后返回一个新的字符串。
让我们逐行解释这个函数的代码:
1. `pub fn capitalize_first(input: &str) -> String {`: 这一行定义了一个公共(`pub`)函数,名称为`capitalize_first`,它接受一个名为`input`的字符串引用(`&str`)作为参数,并将返回一个新的字符串(`String`)。
2. `let mut c = input.chars();`: 这一行创建了一个名为`c`的可变迭代器,它用于迭代输入字符串的字符。`input.chars()` 返回一个字符迭代器,使我们能够逐个访问输入字符串的字符。
3. `match c.next() {`: 这一行使用`match`表达式来检查字符迭代器的下一个元素。
4. `None => String::new(),`: 如果迭代器返回`None`,也就是输入字符串为空,那么函数返回一个新的空字符串。
5. `Some(first) => first.to_uppercase().to_string() + c.as_str(),`: 如果迭代器返回`Some(first)`,也就是输入字符串非空且有至少一个字符,那么它会执行以下操作:
- `first.to_uppercase()`:将第一个字符`first`转换为大写形式。
- `.to_string()`:将大写的字符转换为一个新的字符串。
- `+ c.as_str()`:将这个新的大写字符与剩余的字符拼接起来,`c.as_str()`将剩余字符转换为字符串并连接在一起。
最终,这个函数返回一个新的字符串,其中第一个字符被转换为大写,而其他字符保持不变。如果输入字符串为空,它返回一个空字符串。这个函数可用于将字符串的首字母大写化,例如用于规范化输入文本中的名称或标题。
-Listing 13-15: Calling the map method to create a new iterator and then calling the collect method to consume the new iterator and create a vector
// iterators2.rs
//
// In this exercise, you'll learn some of the unique advantages that iterators
// can offer. Follow the steps to complete the exercise.
//
// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a
// hint.
// Step 1.
// Complete the `capitalize_first` function.
// "hello" -> "Hello"
//capitalize_first("hello"), "Hello"
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
//input.chars() 返回一个字符迭代器,使我们能够逐个访问输入字符串的字符。
// 返回一个字符 怎么变成字符串?
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().to_string() + c.as_str(),
}
}
// Some(first) => first.to_uppercase().to_string() + c.as_str(),: 如果迭代器返回Some(first),也就是输入字符串非空且有至少一个字符,那么它会执行以下操作:
// first.to_uppercase():将第一个字符first转换为大写形式。
// .to_string():将大写的字符转换为一个新的字符串。
// + c.as_str():将这个新的大写字符与剩余的字符拼接起来,c.as_str()将剩余字符转换为字符串并连接在一起。
// what is first
//what is the funciton 它的目的是将输入字符串的第一个字符转换为大写字毅,然后返回一个新的字符串。
// Step 2.
// Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
//vec![]
let v2 = words.iter().map(|x| capitalize_first(x)).collect();
v2
//let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
}
// 这是一个Rust函数,名为`capitalize_words_vector`,它接受一个字符串切片的引用(`&[&str]`),并返回一个包含经过首字母大写化处理后的字符串的`Vec<String>`。
// 让我们逐行解释这个函数的代码:
// 1. `pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {`: 这一行定义了一个公共(`pub`)函数,名称为`capitalize_words_vector`,它接受一个字符串切片的引用`words`作为参数,并将返回一个新的`Vec<String>`,即字符串的向量。
// 2. `words.iter()`: 这一行使用`.iter()`方法创建了一个迭代器,用于遍历输入的字符串切片`words`中的每个元素。
// 3. `.map(|x| capitalize_first(x))`: 在这一行,`.map()`方法将对每个元素应用一个闭包函数。这个闭包函数接受一个字符串引用`x`,然后调用之前定义的`capitalize_first`函数来将字符串的第一个字符大写化,最终返回一个经过处理的字符串。
// 4. `.collect()`: `.collect()`方法将迭代器中的所有处理后的字符串收集到一个新的`Vec<String>`中,并将这个`Vec`作为函数的返回值。
// 综合来看,这个函数的目的是接受一个字符串切片,将其中的每个字符串的首字母大写化,并将处理后的字符串存储在一个新的向量中。这对于批量处理多个单词或短语,并将它们的首字母大写化,例如处理标题或名字列表等情况非常有用。
// Step 3.
// Apply the `capitalize_first` function again to a slice of string slices.
// Return a single string.
// ["hello", " ", "world"] -> "Hello World"
pub fn capitalize_words_string(words: &[&str]) -> String {
words.iter().map(|x| capitalize_first(x)).collect()
}
//01 遍历 wrods 内容放到 string
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(capitalize_first("hello"), "Hello");
}
#[test]
fn test_empty() {
assert_eq!(capitalize_first(""), "");
}
#[test]
fn test_iterate_string_vec() {
let words = vec!["hello", "world"];
assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]);
}
#[test]
fn test_iterate_into_string() {
let words = vec!["hello", " ", "world"];
assert_eq!(capitalize_words_string(&words), "Hello World");
}
}
iterators3.rs
// iterators3.rs
//
// This is a bigger exercise than most of the others! You can do it! Here is
// your mission, should you choose to accept it:
// 1. Complete the divide function to get the first four tests to pass.
// 2. Get the remaining tests to pass by completing the result_with_list and
// list_of_results functions.
//
// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a
// hint.
#[derive(Debug, PartialEq, Eq)]
pub enum DivisionError {
NotDivisible(NotDivisibleError),//8/5
DivideByZero, // 8/0
}
#[derive(Debug, PartialEq, Eq)]
pub struct NotDivisibleError {
dividend: i32,
divisor: i32,
}
// Calculate `a` divided by `b` if `a` is evenly divisible (整除)by `b`.
// Otherwise, return a suitable(合适的) error.
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
//todo!();
if b == 0 {
Err(DivisionError::DivideByZero)
} else if a % b != 0 {
// 创建一个 NotDivisibleError 对象,里面成员是 a b
Err(DivisionError::NotDivisible(NotDivisibleError{dividend: a, divisor: b}))
} else {
Ok(a / b)
}
}
// assert_eq!(divide(81, 9), Ok(9));
// assert_eq!(
// divide(81, 6),
// Err(DivisionError::NotDivisible(NotDivisibleError {
// dividend: 81,
// divisor: 6
// }))
//assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
// Complete the function and return a value of the correct type so the test
// passes.
// Desired output: Ok([1, 11, 1426, 3])
fn result_with_list() -> Result<Vec<i32>, DivisionError> {
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
division_results.collect()
}
//what is ()
// Complete the function and return a value of the correct type so the test
// passes.
// Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)]
// fn list_of_results() -> () {
// let numbers = vec![27, 297, 38502, 81];
// let division_results = numbers.into_iter().map(|n| divide(n, 27));
// division_results.collect()
// //words.iter().map(|x| capitalize_first(x)).collect()
// }
fn list_of_results() -> Vec<Result<i32, DivisionError> > {
let numbers = vec![27, 297, 38502, 81];
let division_results: Vec<Result<i32, DivisionError> > = numbers
.into_iter()
.map(|n| divide(n, 27))
.collect();
division_results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(divide(81, 9), Ok(9));
}
#[test]
fn test_not_divisible() {
assert_eq!(
divide(81, 6),
Err(DivisionError::NotDivisible(NotDivisibleError {
dividend: 81,
divisor: 6
}))
);
}
#[test]
fn test_divide_by_0() {
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
}
#[test]
fn test_divide_0_by_something() {
assert_eq!(divide(0, 81), Ok(0));
}
#[test]
fn test_result_with_list() {
assert_eq!(format!("{:?}", result_with_list()), "Ok([1, 11, 1426, 3])");
}
#[test]
fn test_list_of_results() {
assert_eq!(
format!("{:?}", list_of_results()),
"[Ok(1), Ok(11), Ok(1426), Ok(3)]"
);
}
}
iterators4.rs
// iterators4.rs
//
// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a
// hint.
pub fn factorial(num: u64) -> u64 {
// Complete this function to return the factorial of num
// Do not use:
// - return
// Try not to use:
// - imperative style loops (for, while)
// - additional variables
//要计算给定数字的阶乘而不使用递归、循环或额外变量
// For an extra challenge, don't use:
// - recursion
// 不使用递归
// Execute `rustlings hint iterators4` for hints.
//https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold
//https://www.cuemath.com/numbers/factorial/
(1..=num).fold(1, |acc, x| acc * x)
//let sum = a.iter().fold(0, |acc, x| acc + x);
//olds every element into an accumulator by applying an operation, returning the final result.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn factorial_of_0() {
assert_eq!(1, factorial(0));
}
#[test]
fn factorial_of_1() {
assert_eq!(1, factorial(1));
}
#[test]
fn factorial_of_2() {
assert_eq!(2, factorial(2));
}
#[test]
fn factorial_of_4() {
assert_eq!(24, factorial(4));
}
}
5
// iterators5.rs
//
// Let's define a simple model to track Rustlings exercise progress. Progress
// will be modelled using a hash map. The name of the exercise is the key and
// the progress is the value. Two counting functions were created to count the
// number of exercises with a given progress. Recreate this counting
// functionality using iterators. Try not to use imperative loops (for, while).
// Only the two iterator methods (count_iterator and count_collection_iterator)
// need to be modified.
//
// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a
// hint.
use std::collections::HashMap;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Progress {
None,
Some,
Complete,
}
fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {
let mut count = 0;
for val in map.values() {
if val == &value {
count += 1;
}
}
count
}
fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
// map is a hashmap with String keys and Progress values.
// map = { "variables1": Complete, "from_str": None, ... }
// todo!();
map.values().filter(|&&val| val == value).count()
}
//https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html
//fn values(&self) An iterator visiting all values in arbitrary order. The iterator element type is &'a V.
//pub struct Filter<I, P> { /* private fields */ }
//filter:过滤数据。接受一个闭包并为迭代器中的每个元素调用该闭包。如果闭包返回true,则元素将包含在新的迭代器中。
//https://juejin.cn/post/7221159922905333797
fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
let mut count = 0;
for map in collection {
for val in map.values() {
if val == &value {
count += 1;
}
}
}
count
}
fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
// collection is a slice of hashmaps.
// collection = [{ "variables1": Complete, "from_str": None, ... },
// { "variables2": Complete, ... }, ... ]
// todo!();
collection
.iter()
.flat_map(|map| map.values())
.filter(|&&val| val == value)
.count()
}
// count_collection_iterator 函数的作用是计算在给定的 collection 中,有多少个哈希映射中的值等于指定的 value。
// 函数内部的操作步骤如下:
// collection.iter() 使用迭代器方法获取 collection 中的每个哈希映射的引用。
// flat_map(|map| map.values()) 使用 flat_map 方法将每个哈希映射中的值进行扁平化。这是因为 map.values() 返回一个迭代器,我们需要将这些迭代器的元素合并成一个单一的迭代器,以便后续的操作。
// filter(|&&val| val == value) 使用 filter 方法过滤出那些与 value 相等的值。在这里,我们使用双引用 &&val,因为 map.values() 返回的是引用,而 value 是一个枚举类型,所以需要双引用以进行比较。
// 最后,.count() 方法计算通过前面的过滤器的元素的数量,即与 value 相等的值的数量。
// 因此,count_collection_iterator 函数返回了整个 collection 中所有哈希映射的值中与指定的 value 相等的数量。这是通过使用迭代器方法来实现的,而不需要显式的循环或计数变量。
// #[cfg(test)]
mod tests {
use super::*;
#[test]
fn count_complete() {
let map = get_map();
assert_eq!(3, count_iterator(&map, Progress::Complete));
}
#[test]
fn count_some() {
let map = get_map();
assert_eq!(1, count_iterator(&map, Progress::Some));
}
#[test]
fn count_none() {
let map = get_map();
assert_eq!(2, count_iterator(&map, Progress::None));
}
#[test]
fn count_complete_equals_for() {
let map = get_map();
let progress_states = vec![Progress::Complete, Progress::Some, Progress::None];
for progress_state in progress_states {
assert_eq!(
count_for(&map, progress_state),
count_iterator(&map, progress_state)
);
}
}
#[test]
fn count_collection_complete() {
let collection = get_vec_map();
assert_eq!(
6,
count_collection_iterator(&collection, Progress::Complete)
);
}
#[test]
fn count_collection_some() {
let collection = get_vec_map();
assert_eq!(1, count_collection_iterator(&collection, Progress::Some));
}
#[test]
fn count_collection_none() {
let collection = get_vec_map();
assert_eq!(4, count_collection_iterator(&collection, Progress::None));
}
#[test]
fn count_collection_equals_for() {
let progress_states = vec![Progress::Complete, Progress::Some, Progress::None];
let collection = get_vec_map();
for progress_state in progress_states {
assert_eq!(
count_collection_for(&collection, progress_state),
count_collection_iterator(&collection, progress_state)
);
}
}
fn get_map() -> HashMap<String, Progress> {
use Progress::*;
let mut map = HashMap::new();
map.insert(String::from("variables1"), Complete);
map.insert(String::from("functions1"), Complete);
map.insert(String::from("hashmap1"), Complete);
map.insert(String::from("arc1"), Some);
map.insert(String::from("as_ref_mut"), None);
map.insert(String::from("from_str"), None);
map
}
fn get_vec_map() -> Vec<HashMap<String, Progress>> {
use Progress::*;
let map = get_map();
let mut other = HashMap::new();
other.insert(String::from("variables2"), Complete);
other.insert(String::from("functions2"), Complete);
other.insert(String::from("if1"), Complete);
other.insert(String::from("from_into"), None);
other.insert(String::from("try_from_into"), None);
vec![map, other]
}
}
&& val is what 在 count_iterator 函数中,&&val 是一个双引用,它用于解引用引用的引用。这是由于 map.values() 返回的是一个迭代器,迭代器的元素类型是 &Progress,即对 Progress 枚举的引用。因此,为了进行比较,我们需要解引用两次。
让我来解释一下:
map.values() 返回一个迭代器,它产生类型为 &Progress 的引用。这是因为哈希映射中的值是对 Progress 枚举的引用。
在闭包中,filter(|&&val| val == value) 遍历迭代器中的每个元素。由于每个元素是 &Progress 类型的引用,所以 val 是一个 &Progress 类型的引用。
val 是一个引用,我们需要解引用它两次,以便将其与 value 进行比较。第一次解引用 (&val) 将 &Progress 类型的引用转换为 Progress 枚举的值,第二次解引用 (&&val) 将其再次解引用,以得到 Progress 枚举的实际值。
所以,&&val 是为了解引用两次,以便在过滤中将迭代器中的元素与 value 进行比较。这是因为 Rust 的迭代器通常产生引用,所以我们需要处理引用的引用来获得元素的实际值
本文由mdnice多平台发布