首頁 > 軟體

Rust語言從入門到精通系列之Iterator迭代器深入詳解

2023-08-28 18:05:30

在Rust語言中,迭代器(Iterator)是一種極為重要的資料型別,它們用於遍歷集合中的元素。Rust中的大多數集合型別都可轉換為一個迭代器,使它們可以進行遍歷,這包括陣列、向量、雜湊表等。

使用迭代器可以讓程式碼更加簡潔優雅,並且可以支援一些強大的操作,例如過濾、對映和摺疊等。

在本文中,我們將探討Rust語言中的迭代器的相關知識,並且以我們的老朋友Animal為例,提供相關的範例程式碼。

熟悉Java的Stream和Lambda的同學,學習本章節時,會格外的感覺“親切”。

迭代器的基本概念

迭代器是什麼?

在Rust中,迭代器是一個實現了Iterator trait的型別。該trait定義了一組行為,用於支援遍歷集合中的元素。通過實現Iterator trait,型別可以被轉換為一個迭代器,從而支援Iterate的操作。

Iterator trait

Iterator trait 定義了迭代器的核心行為,它包含了next方法和一些其他方法。next方法返回集合中下一個元素的Option值,直到集合中所有的元素都被遍歷完畢,返回None。

除了next方法之外,Iterator trait 還定義了其他許多有用的方法,比如map、filter等,這些方法可以對迭代器中的元素進行操作和轉換。

pub trait Iterator {
      type Item;
    fn next(&mut self) -> Option<Self::Item>;
    //  多種內建實現方法, skip, map, reduce, collect
    //  和Java中的Stream內建方法非常類似.
}

Animal範例

接下來我們探討實現一個Animal迭代器,Animal實現Iterator trait,使其可以通過迭代器遍歷Animal的各個屬性。 以下是Animal型別的定義:

#[derive(Debug)]
struct Animal {
    name: String,
    age: u32,
    kind: String,
    i:i32,
}

我們可以在Animal上實現Iterator trait,使其可以通過for迴圈進行迭代。

impl Iterator for Animal {
      type Item = String;
    fn next(&mut self) -> Option<Self::Item> {
          let next_attribute = match self.i {
            0 => Some(self.name.clone()),
            1 => Some(self.age.to_string()),
            2 => Some(self.kind.clone()),
            _ => None,
        };
        self.i += 1;
        next_attribute
    }
}

此時,我們已經將我們的型別轉換為迭代器,我們就可以在它上面呼叫各種Iterator trait 的方法。例如,我們可以使用for迴圈遍歷Animal物件的每一個屬性:

#[derive(Debug)]
struct Animal {
    name: String,
    age: u32,
    kind: String,
    i:i32,
}
impl Iterator for Animal {
      type Item = String;
    fn next(&mut self) -> Option<Self::Item> {
          let next_attribute = match self.i {
            0 => Some(self.name.clone()),
            1 => Some(self.age.to_string()),
            2 => Some(self.kind.clone()),
            _ => None,
        };
        self.i += 1;
        next_attribute
    }
}
fn main() {
    let mut animal = Animal {
        name: "Tom".to_string(),
        age : 15,
        kind: "cat".to_string(),
        i : 0
    };
    println!("Name: {}", animal.next().unwrap());
    println!("Age: {}", animal.next().unwrap());
    println!("Kind: {}", animal.next().unwrap());
}
//  輸出結果:
// Name: Tom
// Age: 15
// Kind: cat

在上述程式碼中,我們定義了一個Animal型別的Iterator,並定義了一個名為i的內部狀態變數。該變數用於追蹤遍歷的進度,並決定下一個迭代器值的內容。最終成功列印了animal的全部資訊。

下面繼續我們的學習,定一個Animal向量並遍歷列印每一個Animal的所有屬性:

fn print_all_attributes(animals: Vec<Animal>) {
    for mut animal in animals {
        println!("Name: {}", animal.next().unwrap());
        println!("Age: {}", animal.next().unwrap());
        println!("Kind: {}", animal.next().unwrap());
    }
}
fn main() {
    let animals = vec![Animal {
        name: "Tom".to_string(),
        age : 15,
        kind: "cat".to_string(),
        i : 0
    }];
    print_all_attributes(animals);
}
//  輸出結果:
// Name: Tom
// Age: 15
// Kind: cat

在上述程式碼中,我們使用for迴圈來遍歷所有的Animal物件,並逐一列印它們的屬性。

迭代器的常見用法

map方法

map方法是Iterator trait 中非常重要的一個方法,它可以讓我們對迭代器中的每一個元素進行轉換操作,並返回新的迭代器。例如:

fn main() {
    let animals = vec![Animal {
        name: "Tom".to_string(),
        age : 15,
        kind: "cat".to_string(),
        i : 0
    }, Animal {
        name: "Jerry".to_string(),
        age : 7,
        kind: "mouse".to_string(),
        i : 0
    }];
    let list: Vec<String> = animals
        .into_iter()
        .map(|ani| ani.name.clone())
        .collect();
    println!("{:?}", list)
}
// 輸出 ["Tom", "Jerry"]

上述程式碼中,我們定義了一個包含2個的向量animals,並使用iter方法將其轉換為一個迭代器。然後,我們使用map方法對這個迭代器中的Animal的name操作,返回一個新的迭代器,並使用collect方法將其轉換為向量list。

filter方法

假設我們現在想尋找年齡大於等於3歲的動物,我們可以使用filter方法來實現。

fn main() {
    let animals = vec![Animal {
        name: "Tom".to_string(),
        age : 15,
        kind: "cat".to_string(),
        i : 0
    }];
    let filtered_animals: Vec<Animal> = animals
        .into_iter()
        .filter(|animal| animal.age >= 3)
        .collect();
    println!("{:?}", filtered_animals)
}
//  輸出結果:
//  [Animal { name: "Tom", age: 15, kind: "cat", i: 0 }]

在上述程式碼中,我們使用into_iter方法將Animal向量轉換為迭代器,並使用filter方法過濾其中年齡大於等於3歲的動物,最終返回一個新的Animal向量。

enumerate方法

enumerate方法會將一個迭代器中的元素和它們的索引配對,並返回一個新的迭代器。例如:

fn main() {
    let animals = vec![Animal {
        name: "Tom".to_string(),
        age : 15,
        kind: "cat".to_string(),
        i : 0
    }, Animal {
        name: "Jerry".to_string(),
        age : 7,
        kind: "mouse".to_string(),
        i : 0
    }];
    for (i, animal) in animals.iter().enumerate() {
        println!("{}: {:?}", i, animal);
    }
}
// 輸出:
// 0: Animal { name: "Tom", age: 15, kind: "cat", i: 0 }
// 1: Animal { name: "Jerry", age: 7, kind: "mouse", i: 0 }

上述程式碼中,我們定義了一個包含2個Animal的向量animals,並使用iter方法將其轉換為一個迭代器。然後,我們使用enumerate方法將每Animal與其索引配對,並在for迴圈中列印出來。

flat_map方法

flat_map方法是Iterator trait 中比較少見的方法之一,它可以用於將巢狀的迭代器展開為單個迭代器。例如:

#[derive(Debug, Clone)]
struct Animal {
    name: String,
    age: u32,
    kind: String,
    i: i32,
}
fn main() {
    let cat = Animal {
        name: "Tom".to_string(),
        age: 15,
        kind: "cat".to_string(),
        i: 0,
    };
    let mouse = Animal {
        name: "Jerry".to_string(),
        age: 7,
        kind: "mouse".to_string(),
        i: 0,
    };
    let animals = vec![vec![cat], vec![mouse]];
    let list: Vec<Animal> = animals.iter().flat_map(|x| x.iter().cloned()).collect();
    println!("{:?}", list)
}
// 輸出 [Animal { name: "Tom", age: 15, kind: "cat", i: 0 }, Animal { name: "Jerry", age: 7, kind: "mouse", i: 0 }]

上述程式碼中,我們定義了一個二維向量animals,並使用iter方法將它轉換為迭代器。然後,我們使用flat_map方法將它展開為一個一維的迭代器,並使用collect方法將其轉換為向量list。

zip方法

如果我們需要同時遍歷兩個向量,我們可以使用zip方法進行配對。

fn main() {
    let names = vec!["Tom", "Jerry", "Bob"];
    let ages = vec![3, 4, 5];
    for (name, age) in names.iter().zip(ages.iter()) {
          println!("{} is {} years old.", name, age);
    }
}
//    輸出結果:
// Tom is 3 years old.
// Jerry is 4 years old.
// Bob is 5 years old.

上述程式碼中,我們使用iter方法將names和ages向量轉換為迭代器,並使用zip方法對它們進行配對。對於每一對元素,我們呼叫println!函數並列印它們。

fold方法

fold方法在Rust中也十分重要,它可以接受一個初始值和一個閉包,遍歷迭代器中的每一個元素,並將它們合併成單個值。例如:

fn main() {
    let cat = Animal {
        name: "Tom".to_string(),
        age: 15,
        kind: "cat".to_string(),
        i: 0,
    };
    let mouse = Animal {
        name: "Jerry".to_string(),
        age: 7,
        kind: "mouse".to_string(),
        i: 0,
    };
    let animals = vec![cat, mouse];
    let sum = animals.iter().fold(0, |t, ani| t + ani.age );
    println!("{}", sum)
}
// 輸出 22

上述程式碼中,我們定義了一個包含2個Animal的向量animals,並使用iter方法將其轉換為一個迭代器。然後,我們使用fold方法對這個迭代器中的age進行累加,並返回結果sum。

結論

迭代器是Rust語言中非常重要的資料型別,它們用於遍歷集合中的元素,並支援各種操作。在本教學中,我們探討了迭代器的基本概念和常見用法,以Animal為例子,提供了相應的演示程式碼。希望讀者能夠掌握Rust迭代器的相關內容,並且在實際程式設計中得到應用。

以上就是Rust語言從入門到精通系列之Iterator迭代器深入詳解的詳細內容,更多關於Rust Iterator迭代器的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com