99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

迭代器

2018-08-12 22:03 更新

迭代器

下面我們來(lái)探討一下循環(huán)問(wèn)題。

還記得 Rust 的 for 循環(huán)嗎?下面有一個(gè)例子:

for x in 0..10 {
    println!("{}", x);
}

現(xiàn)在你已經(jīng)知道了更多的 Rust,我們可以詳細(xì)談?wù)勊侨绾喂ぷ鞯?。范?(0 . . 10) 是一個(gè)迭代器。我們可以使用 .next() 方法反復(fù)調(diào)用迭代器,它給出了事情的一個(gè)序列。

如下所示:

let mut range = 0..10;

loop {
    match range.next() {
        Some(x) => {
            println!("{}", x);
         },
        None => { break }
    }
}

我們針對(duì)范圍給出一個(gè)可變的綁定,這就是迭代器。然后用一個(gè)內(nèi)在的 match 進(jìn)行 loop 。用這個(gè) match 操作 range.next() 的結(jié)果,這就給出了到迭代器的下一個(gè)值的一個(gè)引用。next 返回一個(gè) Option,在這種情況下,一旦循環(huán)運(yùn)行完畢,我們會(huì)得到一個(gè)值: Some(i32) 或者 None。如果我們得到 Some(i32),就打印出來(lái),如果我們得到 None,就跳出循環(huán)。

這個(gè)代碼示例和我們的 for 循環(huán)版本基本上是一樣的。for 循環(huán)僅僅是編寫(xiě)loop/match/break 構(gòu)造的一個(gè)方便的方式。

然而,for 循環(huán)不是唯一使用迭代器的情況。編寫(xiě)自己的迭代器包括實(shí)現(xiàn)迭代器的特征。雖然這種操作不是在本指南的范圍之內(nèi),Rust 提供了許多有用的迭代器來(lái)完成各種任務(wù)。在我們談?wù)撨@些之前,我們應(yīng)該談?wù)撘幌?Rust 反模式。這就是范圍的使用方式。

是的,我們剛剛談到范圍很有用。但范圍也很原始。例如,如果你需要遍歷一個(gè) vector 的內(nèi)容,你可能會(huì)這樣寫(xiě):

let nums = vec![1, 2, 3];

for num in &nums {
    println!("{}", num);
}

這比使用一個(gè)真正的迭代器嚴(yán)格的多。你可以直接對(duì) vector 進(jìn)行迭代,像下面寫(xiě)的這樣:

let nums = vec![1, 2, 3];

for num in &nums {
    println!("{}", num);
}

這樣做有兩個(gè)原因。首先,這可以更直接的表達(dá)我們的意思。我們遍歷整個(gè) vector,而不是遍歷索引,然后通過(guò)索引訪(fǎng)問(wèn) vector。第二,這個(gè)版本是更高效的:第一個(gè)版本將有額外的邊界檢查,因?yàn)樗褂昧怂饕?strong>nums[i]。但是因?yàn)槲覀兪褂玫饕来吾槍?duì) vector 的每個(gè)元素產(chǎn)生一個(gè)引用,在第二個(gè)示例中不涉及邊界檢查。這對(duì)于迭代器是很常見(jiàn)的:我們可以忽略不必要的檢查范圍,但仍知道我們是安全的。

關(guān)于 println! 怎樣工作,這里還有一個(gè)細(xì)節(jié)不是 100% 的清楚。num 實(shí)際上是 &i32 類(lèi)型的數(shù)字。也就是說(shuō),它是對(duì) i32 的一個(gè)引用,而不是本身就是 i32,println! 為我們處理非關(guān)聯(lián)化的事物,所以我們不能看到它的細(xì)節(jié)。下面這段代碼同樣工作正常:

let nums = vec![1, 2, 3];

for num in &nums {
    println!("{}", *num);
}

現(xiàn)在我們來(lái)明確非關(guān)聯(lián)化 num。為什么 &num 可以給我們引用?首先,因?yàn)槲覀兠鞔_地使用 & 調(diào)用。其次,如果給我們數(shù)據(jù)本身,我們必須是它的擁有者,它將生成一個(gè)數(shù)據(jù)的副本,并將副本給我們。與引用相比,我們只是借用了引用數(shù)據(jù),所以它只是傳遞一個(gè)引用,而不需要做移動(dòng)。

所以,既然我們已經(jīng)認(rèn)定范圍往往不是你想要的,讓我們來(lái)談?wù)勀阆胍臇|西。

這里主要有3個(gè)類(lèi),它們是彼此相關(guān)的事物:迭代器(iterators),迭代器適配器(iterator adapters),和消費(fèi)者(consumers)。下面給出一些定義:

  • 迭代器給出序列值。
  • 迭代器適配器使用一個(gè)迭代器,產(chǎn)生一個(gè)新的迭代器,它擁有不同的輸出序列。
  • 消費(fèi)者使用一個(gè)迭代器,產(chǎn)生一些值最后的設(shè)置。

首先讓我們來(lái)談?wù)勏M(fèi)者,因?yàn)槟阋呀?jīng)看到一個(gè)迭代器。

消費(fèi)者

consumer 操作一個(gè)迭代器,返回某些類(lèi)型的值。最常見(jiàn)的 consumer 是 collect()。下面的代碼并沒(méi)有完全編譯,但它已經(jīng)顯示了意圖:

let one_to_one_hundred = (1..101).collect();

正如你所看到的,我們可以在我們的迭代器上調(diào)用 collect()。collect() 盡可能多地接收迭代器給它的值,并返回結(jié)果的集合。為什么這不能編譯呢?Rust 不能確定你想收集什么類(lèi)型的值,所以你需要讓它知道。下邊是編譯的版本:

let one_to_one_hundred = (1..101).collect::<Vec<i32>>();

如果你還記得,:: 語(yǔ)法允許我們給出一個(gè)類(lèi)型提示,所以我們可以告訴它,我們需要一個(gè)整數(shù)向量。盡管你并不總是需要使用整個(gè)類(lèi)型。使用一個(gè) _ 可以允許你提供部分提示:

let one_to_one_hundred = (1..101).collect::<Vec<_>>();

即:“請(qǐng)收集 Vec,但為我推斷出T是什么?!币?yàn)檫@個(gè)原因 _ 有時(shí)被稱(chēng)為一種“占位符”。

collect() 是最常見(jiàn)的 consumer,但也存在其他的消費(fèi)者。find()就是其中之一:

let greater_than_forty_two = (0..100)
                            .find(|x| *x > 42);

match greater_than_forty_two {
    Some(_) => println!("We got some numbers!"),
    None => println!("No numbers found :("),
}

find 消耗一個(gè)閉包,針對(duì)迭代器的每個(gè)元素的引用操作。如果元素是我們要找的元素,這個(gè)閉包返回 true,否則返回 false。因?yàn)槲覀兛赡苷也坏揭粋€(gè)匹配的元素,find 會(huì)返回一個(gè) Option 而不是元素本身。

另一個(gè)重要的消費(fèi)者是 fold。下面就是 fold 的示例:

let sum = (1..4).fold(0, |sum, x| sum + x);

fold() 是一個(gè)消費(fèi)者,語(yǔ)法:fold(base, |accumulator, element| ...)。它需要兩個(gè)參數(shù):第一個(gè)是一個(gè)稱(chēng)為 的元素 。第二個(gè)是一個(gè)閉包,本身有兩個(gè)參數(shù):第一個(gè)被稱(chēng)為累加器,第二個(gè)是一個(gè)元素。在每次迭代中,調(diào)用閉包,結(jié)果是在下一次迭代中累加器的值。在第一次迭代中,base 是累加器的值。

好吧,這有點(diǎn)令人困惑。讓我們看看在這個(gè)迭代器中所有事物的值:

累加器 元素 閉包結(jié)果
0 0 1 1
0 1 2 3
0 3 3 6

我們使用這些參數(shù)來(lái)調(diào)用 fold() 函數(shù):

.fold(0, |sum, x| sum + x);

所以,0 是基,sum 是累加器,x 是我們的元素。在第一次迭代中,我們將 sum 設(shè)置為 0,xnums 的第一個(gè)元素 1。然后將 sumx 相加,即 0 + 1 = 1。第二次迭代中,和值成為我們的累加器 sum,元素是數(shù)組的第二個(gè)元素 2,相加,即 1 + 2 = 3 ,這樣就得到了最后一次迭代的累加器的值。在這次迭代中,x 是最后一個(gè)元素 3 ,相加,即 3 + 3 = 6,這就是求和最后的結(jié)果。1 + 2 + 3 = 6,這就是我們最后得到的結(jié)果。

對(duì)于 fold 如果你剛開(kāi)始接觸它,可能會(huì)覺(jué)得這種語(yǔ)法有點(diǎn)奇怪,但一旦開(kāi)始使用它,您會(huì)發(fā)現(xiàn)它的使用范圍很廣,幾乎到處都可以使用。任何時(shí)候,如果你有一系列的事物,而你想要一個(gè)單一的結(jié)果,fold 都是最適當(dāng)?shù)摹?/p>

由于迭代器存在另一個(gè)我們還沒(méi)有談到屬性:懶惰,消費(fèi)者就變得尤為重要。讓我們多談?wù)撘恍╆P(guān)于迭代器的問(wèn)題,你就會(huì)明白消費(fèi)者為什么如此重要。

迭代器

正如之前說(shuō)過(guò)的,我們可以使用 .next() 方法反復(fù)調(diào)用一個(gè)迭代器,它給出了一個(gè)事情的序列。因?yàn)槟阈枰{(diào)用這個(gè)方法,這意味著迭代器可以偷懶,而不是預(yù)先生成的所有值。例如,在這段代碼中,實(shí)際上并沒(méi)有生成 1-100的數(shù)字,它僅僅代表了一個(gè)序列,而不是產(chǎn)生一個(gè)值:

let nums = 1..100;

因?yàn)槲覀儧](méi)有針對(duì)范圍做任何事情,它不會(huì)生成序列。下面讓我們加入消費(fèi)者:

let nums = (1..100).collect::<Vec<i32>>();

消費(fèi)者 collect() 要求范圍給它一些數(shù)字,這樣它才會(huì)做生成序列的工作。

您將看到范圍是兩個(gè)基本的迭代器之一。另一種是 iter()。iter() 可以把一個(gè) vector 變成一個(gè)簡(jiǎn)單的迭代器,反過(guò)來(lái)這個(gè)迭代器給出每個(gè)元素:

let nums = vec![1, 2, 3];

for num in nums.iter() {
    println!("{}", num);
}

這兩種基本迭代器應(yīng)該能很好地為你服務(wù)。有一些更高級(jí)的迭代器,包括那些不限范圍的。

這里談?wù)摰降牡饕呀?jīng)足夠你使用。迭代器適配器是我們需要談?wù)摰年P(guān)于迭代器最后的概念。

迭代器適配器

迭代器適配器獲得一個(gè)迭代器,以某種方式對(duì)其進(jìn)行修改,產(chǎn)生一個(gè)新的迭代器。最簡(jiǎn)單的一個(gè)叫做 map

(1..100).map(|x| x + 1);

map 被另一個(gè)迭代器調(diào)用,產(chǎn)生一個(gè)新的迭代器,在新的迭代器中,每個(gè)元素引用迭代器給出的關(guān)閉作為調(diào)用它的參數(shù)。這將打印出 2-100 的數(shù)字。如果你編譯這個(gè)示例,您會(huì)得到一個(gè)警告:

warning: unused result which must be used: iterator adaptors are lazy and
        do nothing unless consumed, #[warn(unused_must_use)] on by default
(1..100).map(|x| x + 1);
 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

迭代器的懶惰又起作用了!這個(gè)閉包永遠(yuǎn)不會(huì)執(zhí)行。這個(gè)例子也不會(huì)打印任何數(shù)字:

(1..100).map(|x| println!("{}", x));

如果你只是為了測(cè)試其副作用,而在一個(gè)迭代器上執(zhí)行一個(gè)閉包,那只需要使用 for 就好了。

有很多有趣的迭代器適配器。take(n) 將在原迭代器的 n 個(gè)元素的基礎(chǔ)上返回一個(gè)新的迭代器。注意,這個(gè)對(duì)于原迭代器沒(méi)有副作用。讓我們使用一下之前提到的無(wú)限迭代器:

for i in (1..).step_by(5).take(5) {
    println!("{}", i);
}

這將打印出

1
6
11
16
21

filter() 是一個(gè)以一個(gè)閉包作為參數(shù)的適配器。這個(gè)閉包返回 true 或者 false。新的迭代器 filter() 產(chǎn)生唯一的元素,閉包返回true:

for i in (1..100).filter(|&x| x % 2 == 0) {
    println!("{}", i);
}

這將打印 1 到 100 之間的所有的偶數(shù)。(注意:因?yàn)?filter 不消耗將遍歷的元素,它只是傳遞每一個(gè)元素的引用,從而可以使用 &x 模式過(guò)濾謂詞來(lái)提取整數(shù)本身。)

現(xiàn)在你可以將三件事放在一起考慮:首先是一個(gè)迭代器,經(jīng)過(guò)幾次調(diào)整,然后消耗這個(gè)結(jié)果。檢查一下:

(1..1000)
    .filter(|&x| x % 2 == 0)
    .filter(|&x| x % 3 == 0)
    .take(5)
    .collect::<Vec<i32>>();

這將給你一個(gè)包含 6、12、18、24 和 30 的向量。

這只是一個(gè)關(guān)于迭代器,迭代器適配器,和消費(fèi)者的小的嘗試。有很多非常有用的迭代器,您也可以編寫(xiě)您自己的迭代器。迭代器提供一個(gè)安全、有效的方式來(lái)操作各種列表。起初你會(huì)覺(jué)得它們有點(diǎn)不同尋常,但一旦你開(kāi)始使用它們,你就會(huì)迷上它們。關(guān)于迭代器和消費(fèi)者的不同點(diǎn)的完整列表,你可以查閱迭代器模塊文檔。

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)