Rustlings Part19: — iterators in Rust

260 阅读10分钟

LearningOS/rust-based-os-comp2023

Rustlings Part19: — iterators in Rust

You can do it

ExerciseBook 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
conversionsn/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多平台发布