隱私權政策

搜尋此網誌

技術提供:Blogger.

關於我自己

我的相片
目前從事軟體相關行業,喜歡閱讀、健身、喝調酒。習慣把遇到的問題記下來,每天做一些整理方便自己以後查。 Python、Rust、Kotlin等程式語言皆為自學,目前比較著重在Rust語言,歡迎一起討論。

2023年11月22日 星期三

Rust 迭代for_each概述 - 對每個元素執行指定操作


最近每次都想要把程式縮減

有時候想要對元素操作

第一個會想到的就是map[1]

但map需要有一個返還值

也就是需要打

let x = num.iter().map(||);

類似這樣子的式子

如果是不需要返還值的操作可以用for_each


for_each

for_each被歸類在迭代器下

文件[2]這樣寫

對迭代器中的每個元素進行指定操作

    fn for_each<F>(self, f: F)
    where
        Self: Sized,
        F: FnMut(Self::Item),

self為迭代器

f為閉包,包含一元素

在大部分的情況下

for_each可以用for迴圈呈現

for迴圈會比較簡單易懂,for_each會有迭代的風格


例子

舉個例子

假設有個數組

希望println數組裡面元素的平方

通常會使用for迴圈這樣打

    let nums = vec![1, 2, 3, 4, 5];
    for num in nums {
        let square = num * num;
        println!("The square value: {}", square);
    }
The square value: 1 The square value: 4 The square value: 9 The square value: 16 The square value: 25

使用for迴圈要打多行指令

才可以達成要的功能

不優雅,太不優雅了


於是有個優雅的功能出現

使用for_each可以這樣打

    let nums = vec![1, 2, 3, 4, 5];
    nums.iter().for_each(|num| {
        let square = num * num;
        println!("The square value: {}", square);
    })
The square value: 1 The square value: 4 The square value: 9 The square value: 16 The square value: 25

第一行為數組

透過iter將其放入迭代器

並且適用for_each對每個元素操作

閉包|num|表示元素值

square為 num * num,之後呈現


for_each跟for一樣,也可以改變原本數組的數值

    let mut nums = vec![1, 2, 3, 4, 5];
    nums.iter_mut().for_each(|num: &mut i32| {
        *num = *num * *num;
    });
    println!("The square value: {:?}", nums); The square value: [1, 4, 9, 16, 25]

透過iter_mut[3]進行可變引用迭代

對*num解引用,並把值設成num * num

用for迴圈的話則是這樣操作

    let mut nums = vec![1, 2, 3, 4, 5];
    for num in nums.iter_mut() {
        *num = *num * *num;
    }
    println!("The square value: {:?}", nums);
The square value: [1, 4, 9, 16, 25]



應用

for_each的文件中舉了一個比較難的例子

就用這題當作範例


首先得要介紹mpsc::channel[4]

文件解釋,創建一個非同步線程,回傳接收方與發送方

...

我的部落格比較希望盡量都可以看懂

畢竟當初新手的時候真覺得很多名詞都不懂

講簡單一點

channel就是會創造一個傳送方(tx)與接收方(rx)

傳送方可以傳送資料,讓遠在天邊的接收方收到資料

特點是可以讓不同程式碼之間傳送資料

至於非同步通道就之後在講


    use std::sync::mpsc::channel;

    let (tx, rx) = channel();
    (0..5).map(|x| x * 2 + 1)
          .for_each(move |x| tx.send(x).unwrap());
   
    let v: Vec<_> = rx.iter().collect();
    assert_eq!(v, vec![1, 3, 5, 7, 9]);

因此一行一行介紹程式碼

第一行導入channel

第三行,透過使用channel創建傳送方(tx)與接收方(rx)

第四行(0..5)表示創建一個0到5的數組,也就是[0, 1, 2, 3, 4]

透過map將數組中的值乘2加1

第五行,透過for_each將x的值傳送出去

第七行,rx接收到tx傳送的x,並將全部蒐集起來放到數組中

最後比較結果


至於第五行有個move[5]

閉包沒辦法使用閉包外的參數

如果要使用,必須使用move將外部的參數放進去

這在閉包、非同步線程很常使用

之後有空會把move和非同步線程講更細一點


結論

有時候用傳統for迴圈會比較直觀

有時候用for_each會比較容易閱讀

我覺得這取決於工程師

看怎樣打會比較開心

我自己單人作業時是喜歡用for_each

因為把程式塞成一行,很帥

特別是在Leetcode、Codewar之類的,更帥

但如果是整個團隊要一起打程式就會看情況了





參考資料

[1] https://lageeblog.blogspot.com/2023/10/rust-map.html

[2] https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.for_each

[3] https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut

[4] https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html

[5] https://doc.rust-lang.org/std/keyword.move.html

0 comments:

張貼留言