Rust語言教程(3) - 數(shù)組與向量

Rust語言教程(3) - 數(shù)組與向量

上一節(jié)我們采摘了不少低矮的果實,將其它語言學到的知識遷移到Rust 中來。這一節(jié)我們?nèi)匀焕^續(xù)采摘。
在數(shù)據(jù)結構中,最經(jīng)常使用的就是定長的數(shù)組和變長的向量。

數(shù)組

Rust的數(shù)組除了是把類型和長度放在一個方括號里之外,沒有什么特別的。我們直接上例子:

    let mut a_101 : [i32;3] = [0,0,0];
    a_101[0] = 1;
    println!("{:?}",a_101);

如果數(shù)組比較長,列舉起來比較麻煩,可以采用"[值|長度]"這樣的格式來進行初始化:

    let mut a_102 : [i64;100] = [0i64;100];
    a_102[99] = 100_i64;
    println!("{:?}",a_102);

切片

我們可以使用切片來對數(shù)組的內(nèi)容進行操作。切片默認不能修改數(shù)據(jù),所以也不需要獲取所有權,使用起來跟我們熟悉的語言差不多。

可以采用"[n..]"的方式,取從第n個元素開始到結尾的切片。

例:

    let a_103 = &a_102[1..];
    println!("{:?}",a_103);

這樣a_103獲取的是一個99個元素的切片。
雖然a_102數(shù)組本身是mut可以修改的,但是切片a_103是只讀的。

比如我們想給a_103中的下標賦值,就會報錯:

174 |     a_103[0] = 1;
    |     ^^^^^^^^^^^^ `a_103` is a `&` reference, so the data it refers to cannot be written

解決方案也很簡單,給切片也加上mut就好了。我們看個例子:

    let a_104 = &mut a_102[..10];
    a_104[0] = 0xFF;
    println!("{:?}",a_104);

輸出結果為:

[255, 1, 2, 3, 4, 5, 6, 7, 8, 9]

多維數(shù)組

數(shù)組的元素仍然可以是數(shù)組,這樣就構成了多維數(shù)組。

比如我們先來一個二維的:

let mut a_105 = [[1,2],[3,4]];

我們再堆一個三維的:

    let mut a_106 : [[[i32;2];2];2] = [[[0x55AA;2];2];2];
    println!("{:?}",a_106);

輸出結果為:

[[[21930, 21930], [21930, 21930]], [[21930, 21930], [21930, 21930]]]

元素的訪問方法也是我們熟悉的方式,我們來看個例子:

    let mut a_105 = [[1,2],[3,4]];
    a_105[0]= [5,5];
    a_105[1][1] = 1i32;
    println!("{:?}",a_105);

    let mut a_106 : [[[i32;2];2];2] = [[[0x55AA;2];2];2];
    a_106[0][0][0]= 0x10;
    println!("{:?}",a_106);

輸出結果為:

[[5, 5], [3, 1]]
[[[16, 21930], [21930, 21930]], [[21930, 21930], [21930, 21930]]]

向量

數(shù)組一旦在編譯期確定,容量就不可變。如果需要動態(tài)增刪元素,可以使用向量。
向量使用起來很方便,通過vec!宏就可以生成一個空的容器,然后用push方法添加新元素。

我們看個例子:

    let mut vec1 = vec!();
    vec1.push(1);
    vec1.push(2);
    println!("{:?}",vec1);

輸出結果為:

[1, 2]

我們也可以使用序列來生成向量:

    let mut vec2 :Vec<i32> = (1..=10).collect();
    println!("{:?}",vec2);

輸出結果為:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

向量的長度和容量

與大家可能使用過的容器類似,Rust的向量也有長度和容量兩個屬性。長度表示當前容器中的元素數(shù)量,而容量表示最大容納的元素數(shù)。

我們把上面例子中的向量的長度和容量打印一下:

    let mut vec1 = vec!();
    vec1.push(1);
    vec1.push(2);
    println!("{:?} {} {}",vec1, vec1.len(),vec1.capacity());

我們使用vec!宏而不是直接調(diào)用Vec::new其實就隱含了預分配容量的考慮。

如果我們想手動設置初始的容量的話,可以使用Vec的with_capacity方法。

let mut vec3 = Vec::with_capacity(10);

如果想把容量變小,可以再調(diào)用truncate方法將其變小,多余的數(shù)據(jù)將被拋棄。如果truncate的大小超過當前容量,則什么也不會發(fā)生。

    let mut vec3 = Vec::with_capacity(10);
    vec3.push(-1.0);
    vec3.truncate(2);
    println!("{:?}",vec3);

向量的切片

向量同樣可以用切片進行訪問和修改元素,我們看個例子:

    let vs1 = &mut vec1;
    vs1[0] = 100;
    println!("{:?}",vs1);

向量的插入

向量作為一個動態(tài)數(shù)據(jù)結構,當然不只是從末端插入這一種做法,可以從任意位置使用insert方法添加元素。
例:

    vec1.insert(0,100);

向量刪除元素

有push,對應就有pop,刪除末尾的元素。

我們還以之前的例子為例,pop會將最后push進來的2給pop出來:

    let mut vec1 = vec!();
    vec1.push(1);
    vec1.push(2);
    vec1.insert(0,100);
    let e1 = vec1.pop();
    println!("{:?} {} {}",vec1, vec1.len(),vec1.capacity());
    println!("{:?}",e1);

輸出結果為:

[100, 1] 2 4
Some(2)

Some是可選對象,熟悉ocaml的同學應該不陌生,這是一種支持可能為空的結構。

對應于insert的是remove方法,可以指定刪除某一位置上的元素。

例:

vec1.remove(1);

與pop不同的是, remove并不返回被刪除的對象,刪了就是沒了。

最后是clear方法,將所有元素都清除掉,恢復成空向量:

vec1.clear()

只保留符合條件的元素

向量提供了retain方法用于只保留符合條件的元素。

比如我們只想保留偶數(shù):

    let mut vec2 :Vec<i32> = (1..=10).collect();
    vec2.retain(|&x| x % 2 == 0);
    println!("{:?}",vec2);

輸出結果為:

[2, 4, 6, 8, 10]

交換元素

Vec支持swap方法用于交換兩個位置上的元素:

    let mut vec2 :Vec<i32> = (1..=10).collect();
    vec2.retain(|&x| x % 2 == 0);
    vec2.swap(1,2);
    println!("{:?}",vec2);

輸出結果就變成:

[2, 6, 4, 8, 10]

反轉

Vec支持reverse方法將列表反序,我們還在上面的例子上改造:

    let mut vec2 :Vec<i32> = (1..=10).collect();
    vec2.retain(|&x| x % 2 == 0);
    vec2.swap(1,2);
    vec2.reverse();
    println!("{:?}",vec2);

輸出的結果變成:

[10, 8, 4, 6, 2]

排序

Vec支持sort方法對向量進行排序,目前的實現(xiàn)方法是歸并排序的一種timsort。這是一種穩(wěn)定排序,也就是對于值相同的元素,它們之間的原始順序不會改變。

    let mut vec2 :Vec<i32> = (1..=10).collect();
    vec2.retain(|&x| x % 2 == 0);
    vec2.swap(1,2);
    vec2.reverse();
    vec2.sort();
    println!("{:?}",vec2);

結果又恢復成了[2, 6, 4, 8, 10]。

如果想用快速排序這種不穩(wěn)定排序的話,Vec也支持一種快速排序的變種unstable_sort方法。這是結合了隨機化快速排序和堆排序的一種排序方法,有興趣的同學可以看下原理:https://github.com/orlp/pdqsort

例:

vec2.sort_unstable();

這里需要注意一點Rust特色的東西,就是浮點數(shù)不能直接調(diào)用上面的兩個排序方法。因為浮點數(shù)中有NaN,而NaN是沒有偏序關系的。

口說無憑,我們實際來看一下:

    let mut vec_f = vec!();
    let mut v_1 = 1.0f64;
    for i in 1..=10 {
        vec_f.push(v_1);
        v_1 += 1.0;
    }
    vec_f.sort();

運行結果如下:

error[E0277]: the trait bound `f64: Ord` is not satisfied
   --> src/main.rs:222:11
    |
222 |     vec_f.sort();
    |           ^^^^ the trait `Ord` is not implemented for `f64`

sort需要Ord trait,而f64類型因為NaN的緣故實現(xiàn)不了Ord trait. trait可以先理解為其它語言中的接口或者抽象類。

這咋辦呢?特事特辦。針對浮點數(shù)的集合來說,偏序是實現(xiàn)不了的,但是針對要排序的向量,只要其中不含NaN,我們就可以對其進行排序:

    let mut vec_f = vec!();
    let mut v_1 = 1.0f64;
    for i in 1..=10 {
        vec_f.push(v_1);
        v_1 += 1.0;
    }
    vec_f.sort_by(|a, b| b.partial_cmp(a).unwrap());
    println!("{:?}",vec_f);

輸出結果為:

[10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0]

小結

上節(jié)我們學習了Rust的基本類型和流程控制語句,加上本節(jié)的不可變和可變兩種容器數(shù)組和向量,有很多邏輯上的代碼已經(jīng)可以開始寫了。而且應該還很舒適,沒有太體會到Rust與其它語言的不同。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容