跳轉到內容

Rust 新手程式設計師/陣列和向量

100% developed
來自華夏公益教科書

現在假設我們要儲存一些數字並找到它們的中間值。作為統計學 101 的複習,中間值是在排序後位於中心的數值。例如,在 9、3、8、2、7 中,中間值為 7,因為 2 和 3 是最低的兩個,8 和 9 是最高的兩個,而 7 位於中間。現在的問題是如何儲存這些數字?我們可以這樣做

 fn main() {
     let number1 = 9;
     let number2 = 3;
     let number3 = 8;
     let number4 = 2;
     let number5 = 7;
 }

然而,這有很多缺點。我們必須記住並使用變數名;我們必須直接引用它們,並且沒有簡單的方法可以遍歷每個數字,而無需手動操作。這就是為什麼存在一種內建方法來處理這些問題,稱為陣列

 fn main() {
     let numbers = [9, 3, 8, 2, 7];
 }

陣列是事物的列表,經過組織,以便你可以使用列表中的順序來引用它。需要注意的是,陣列中的第一個元素被稱為第 0 個元素,你可以透過使用方括號來獲取它。陣列的型別也是 [事物的型別; 事物的數量]。我們可以明確地寫出來,如下所示

 fn main() {
     let numbers: [i32; 5] = [9, 3, 8, 2, 7];
     println!("{}", numbers[0]);
 }

這將輸出 9,因為它是在 numbers 的第一個元素。假設我們要列印 numbers 中的所有元素,我們該如何做到呢?我們可以使用上一頁的工具,即 for 迴圈和範圍。要獲取 numbers 中元素的數量,可以使用 numbers.len()。這是一個型別的函式,因此你可以使用它在任何陣列之後使用點來獲取它的長度。我們確實在上面有長度,但是如果我們改變元素數量,.len() 仍然可以正常工作,因此這有助於保持易於改變。有了這一切,以下是迴圈遍歷陣列並列印所有值的程式碼

 fn main() {
     let numbers = [9, 3, 8, 2, 7];
     for index in 0..numbers.len() {
         println!("{}", numbers[index]);
     }
 }

索引是與陣列中值相對應的陣列編號的名稱。使用 [] 方括號來獲取值被稱為索引到陣列中。請注意,因為陣列從 0 開始,並且因為範圍是非包含的,所以索引從 0 到 4,但不包括 5。如果我們嘗試執行

 fn main() {
      let numbers = [9, 3, 8, 2, 7];
      println!("{}", numbers[5]);
 }

我們的編譯器會抱怨此操作將在執行時引發 panic。panic 是指在 Rust 中執行了非法操作時會發生的事件,因此程式會停止執行。你永遠不想發生 panic,因此我們的編譯器試圖幫助我們!Rust 編譯器非常智慧,會幫助我們很多。

所以現在我們要編寫一個函式來獲取一個數值陣列的中間值

 fn get_median(input: [i32; 5]) -> i32 {
     //to do
 }

最明顯的方法是排序陣列,使其有序,然後獲取中間值。我們如何排序陣列?一個簡單的方法是從左到右遍歷,然後再次從左到右遍歷,如果右側的數字更大,則交換它們。這是一個很難理解的概念,所以以下是程式碼

 fn get_median(input: [i32; 5]) -> i32 {
     let mut array = input;
     //sort array
     for index1 in 0..array.len() {
         for index2 in index1..array.len() {
             if array[index2] > array[index1] {
                 array.swap(index1, index2);
             }
         }
     }
     //to do, return median
 }

注意另一個 for 迴圈中的 for 迴圈。這被稱為巢狀 for 迴圈,這意味著對於陣列中的每個值,所有其他值都會與它進行比較。此外,內部 for 迴圈的範圍從外部迴圈的索引開始。這樣做是為了避免比較已經比較過的值,這並非絕對必要,但這意味著我們不會執行不必要的步驟。array.swap() 函式與之前的 .len() 函式類似,但它接受兩個引數,這兩個引數是陣列中的索引(index 的複數),並將這兩個值交換。
整個過程被稱為演算法,它是一系列解決問題的步驟。排序陣列是一個經典的演算法,因為它非常常見,而這是一種非常簡單的演算法。
接下來,我們應該返回陣列的中間值。中間值是陣列的中心點,所以我們只需要執行 array.len() / 2 就可以了?好吧,如果長度是奇數,因為它是整數除法,所以我們會得到一個整數,這將適用於陣列的中心點。對於這個例子,5 / 2 = 2,進行整數除法,這是陣列的中點。但是,如果陣列的長度是偶數,那麼中間值就是中間兩個值的平均值。要獲取兩個值的平均值,我們只需將它們加在一起併除以 2。這意味著我們的最終 get_median 函式是

 fn get_median(input: [i32; 5]) -> i32 {
     let mut array = input;
     //sort array
     for index1 in 0..array.len() {
         for index2 in index1..array.len() {
             if array[index2] > array[index1] {
                 array.swap(index1, index2);
             }
         }
     }
     //return median
     if array.len() % 2 == 0 {
         //even
         let middle_value1 = array[array.len() / 2 - 1];
         let middle_value2 = array[array.len() / 2];
         let median = (middle_value1 + middle_value2) / 2;
         return median;
     } else {
         //odd
         let median = array[array.len() / 2];
         return median;
     }
 }

注意,我們在將中間值加在一起併除以 2 時,在中間值周圍添加了括號 ()。這是因為 Rust 中的數學遵循操作順序,因此除法在加法之前發生。因為我們希望加法先發生,所以我們在周圍新增括號來告訴 Rust 它先發生。我們還使用 value % 2 == 0 來檢查長度是否可被 2 整除,也就是奇數或偶數。接下來,如果我們將它插入我們的 main 中

 fn main() {
     let numbers = [9, 3, 8, 2, 7];
     let median = get_median(numbers);
     println!("{}", median);
 }

我們得到了我們一開始計算出的 7!

但是,如果我們要使用不同大小的陣列呢?請注意,我們在 get_median() 函式的引數型別中必須包含 5。陣列非常有限,它們必須始終具有我們在執行程式之前所說的確切元素數量。這也意味著我們編寫的函式只能接受大小為 5 的陣列。解決這個問題的方法是使用向量。向量與陣列類似,它按順序儲存一堆相同型別的元素,但不同的是它們可以增長和縮小,並且可以包含不同數量的元素。建立向量的最簡單方法是使用 vec![],如下所示

 fn main() {
     let numbers = vec![9, 3, 8, 2, 7];
     let median = get_median(numbers);
     println!("{}", median);
 }

請注意,我們再次在末尾使用 !,就像 println! 一樣。 ! 表示這是一個宏。宏稍微複雜一些,稍後會解釋。現在,像這樣使用 vec![] 將生成一個與之前陣列相同的向量。然後我們將 get_median 函式更改為

 fn get_median(input: Vec<i32>) -> i32 {
     //the inside of the function stays the same
 }

我們只需更改輸入的型別,它就可以正常工作。請注意,我們必須在 Vec 之後使用 <> 來表示型別。此外,我們不需要在型別中寫入元素數量,因為向量可以改變其大小。現在,我們可以更改 main() 來嘗試不同的數字集

 fn main() {
     let numbers1 = vec![9, 3, 8, 2, 7];
     println!("{}", get_median(numbers1));
     let numbers2 = vec![5, 3, 5, 7, 6, 9, 10, 9];
     println!("{}", get_median(numbers2));
     let numbers3 = vec![1, 40, 26, 3];
     println!("{}", get_median(numbers3));
 }

在執行它之後,我們得到:7、6 和 14?

等等,不對,7 是正確的,但第二個的中間值應該是 6.5,因為 6 和 7 的平均值。最後一個應該是 14.5,因為 3 和 26 的平均值。這是因為我們在這裡進行整數除法,因此它給了我們整數的值,這不是我們想要的。這引出了下一個主題:數字轉換。

接下來:數字轉換

華夏公益教科書