Move 中的迴圈結構 (Loop Constructs in Move)
許多程式需要對值進行迭代,而 Move 提供 while 與 loop 形式,讓你能在這些情境中撰寫程式碼。此外,你也可以在執行期間使用 break(離開迴圈)與 continue(略過本次迭代的其餘部分,並回到控制流程結構頂端)來修改這些迴圈的控制流程。
while 迴圈 (while Loops)
while 結構會重複執行主體(型別為 unit 的運算式),直到條件(型別為 bool 的運算式)求值為 false。
以下是一個簡單的 while 迴圈範例,用於計算從 1 到 n 的數字總和:
fun sum(n: u64): u64 {
let mut sum = 0;
let mut i = 1;
while (i <= n) {
sum = sum + i;
i = i + 1
};
sum
}
也允許無限 while 迴圈:
fun foo() {
while (true) { }
}
使用巨集而非迴圈,是達成更簡潔且易讀目的的更佳方式。 本文僅以上述函式 sum 為例,體驗巨集函式的魅力:
fun sum(n: u64): u64 {
vector::tabulate!(n, |i| i + 1).fold!(0, |sum, num| sum + num)
}
在 while 迴圈內使用 break (Using break Inside of while Loops)
在 Move 中,while 迴圈可以使用 break 提早離開。例如,假設我們正在尋找某個值在向量中的位置,並希望在找到它時 break:
fun find_position(values: &vector<u64>, target_value: u64): Option<u64> {
let size = values.length();
let mut i = 0;
let mut found = false;
while (i < size) {
if (values[i] == target_value) {
found = true;
break
};
i = i + 1
};
if (found) {
option::some(i)
} else {
option::none<u64>()
}
}
在此,如果借用的向量值等於目標值,我們便將 found 旗標設為 true,接著呼叫 break,使程式離開迴圈。
最後請注意,while 迴圈的 break 無法接受值:while 迴圈一律回傳 unit 型別 (),因此 break 也是如此。
在 while 迴圈內使用 continue (Using continue Inside of while Loops)
與 break 類似,Move 的 while 迴圈可以呼叫 continue 來略過部分迴圈主體。這讓我們能在條件不符合時略過部分計算,如以下範例所示:
fun sum_even(values: &vector<u64>): u64 {
let size = values.length();
let mut i = 0;
let mut even_sum = 0;
while (i < size) {
let number = values[i];
i = i + 1;
if (number % 2 == 1) continue;
even_sum = even_sum + number;
};
even_sum
}
此程式碼會迭代所提供的向量。對於每個項目,若該項目是偶數,便會將其加入 even_sum。但若不是,則會呼叫 continue,略過加總運算並回到 while 迴圈的條件檢查。
loop 運算式 (loop Expressions)
loop 運算式會重複執行迴圈主體(型別為 () 的運算式),直到遇到 break:
fun sum(n: u64): u64 {
let mut sum = 0;
let mut i = 1;
loop {
i = i + 1;
if (i >= n) break;
sum = sum + i;
};
sum
}
若沒有 break,迴圈將會永遠持續。在以下範例中,程式會永遠執行,因為 loop 沒有 break:
fun foo() {
let mut i = 0;
loop { i = i + 1 }
}
在 loop 中搭配值使用 break (Using break with Values in loop)
不同於一律回傳 () 的 while 迴圈,loop 可以透過 break 回傳一個值。如此一來,整體 loop 運算式便會求值為該型別的值。例如,我們可以使用 loop 與 break 重寫上述的 find_position,若找到該值便立即回傳其索引:
fun find_position(values: &vector<u64>, target_value: u64): Option<u64> {
let size = values.length();
let mut i = 0;
loop {
if (values[i] == target_value) {
break option::some(i)
} else if (i >= size) {
break option::none()
};
i = i + 1;
}
}
此迴圈會以 option 結果中斷,而作為函式主體中的最後一個運算式,它會產生該值作為最終函式結果。
在 loop 運算式內使用 continue (Using continue Inside of loop Expressions)
如你所預期,continue 也能在 loop 內使用。以下是先前的 sum_even 函式,改為使用帶有 break and continue 的 loop,而非 while。
fun sum_even(values: &vector<u64>): u64 {
let size = values.length();
let mut i = 0;
let mut even_sum = 0;
loop {
if (i >= size) break;
let number = values[i];
i = i + 1;
if (number % 2 == 1) continue;
even_sum = even_sum + number;
};
even_sum
}
while 與 loop 的型別 (The Type of while and loop)
在 Move 中,迴圈是具有型別的運算式。while 運算式一律具有型別 ()。
let () = while (i < 10) { i = i + 1 };
若 loop 包含 break,該運算式便具有 break 的型別。不帶值的 break 具有 unit 型別 ()。
(loop { if (i < 10) i = i + 1 else break }: ());
let () = loop { if (i < 10) i = i + 1 else break };
let x: u64 = loop { if (i < 10) i = i + 1 else break 5 };
let x: u64 = loop { if (i < 10) { i = i + 1; continue} else break 5 };
此外,若迴圈包含多個 break,它們都必須回傳相同型別:
// 無效 -- 第一個 break 回傳 (),第二個回傳 5
let x: u64 = loop { if (i < 10) break else break 5 };
若 loop 沒有 break,則 loop 可以具有任何型別,如同 return、abort、break 與 continue。
(loop (): u64);
(loop (): address);
(loop (): &vector<vector<u8>>);
若你需要更精確的控制流程,例如跳出巢狀迴圈,下一章將介紹 Move 中具標籤控制流程的使用方式。