メインコンテンツまでスキップ

While, For, ループ

Moveは whileforloopの3つのループ構造を提供します。

whileループ

このwhile構文は、条件(boolの型の式)がfalseと評価されるまで、本体(unitの型の式)を繰り返します。

以下は1からnまでの数値の合計を計算する単純なwhileループの例です。

script {
fun sum(n: u64): u64 {
let sum = 0;
let i = 1;
while (i <= n) {
sum = sum + i;
i = i + 1
};

sum
}
}

無限ループが許可されます:

script {
fun foo() {
while (true) { }
}
}

break

break式は、条件がfalseと評価する前にループを終了するために使用出来ます。例えば、以下のループはbreakを使用し、1より大きいnの最小の因数を見つけます。

script {
fun smallest_factor(n: u64): u64 {
//入力が0もしくは1ではないと仮定します
let i = 2;
while (i <= n) {
if (n % i == 0) break;
i = i + 1
};

i
}
}

break式はループの外部では使用できません。

continue

continue式はループの残りをスキップし、次の反復処理へ進みます。 このループは、数値が10で割り切れる場合を除き、continueを使用して1, 2, ..., nの合計を計算します。

script {
fun sum_intermediate(n: u64): u64 {
let sum = 0;
let i = 0;
while (i < n) {
i = i + 1;
if (i % 10 == 0) continue;
sum = sum + i;
};

sum
}
}

continue式はループの外部では使用出来ません。

breakcontinueの型

breakcontinuereturnabortと同様、任意の型を持つ事が出来ます。以下の例で、この柔軟な型付けが役立つ所を示します。

script {
fun pop_smallest_while_not_equal(
v1: vector<u64>,
v2: vector<u64>,
): vector<u64> {
let result = vector::empty();
while (!vector::is_empty(&v1) && !vector::is_empty(&v2)) {
let u1 = *vector::borrow(&v1, vector::length(&v1) - 1);
let u2 = *vector::borrow(&v2, vector::length(&v2) - 1);
let popped =
if (u1 < u2) vector::pop_back(&mut v1)
else if (u2 < u1) vector::pop_back(&mut v2)
else break; // ここで、`break`の型は`u64`です
vector::push_back(&mut result, popped);
};

result
}
}
script {
fun pick(
indexes: vector<u64>,
v1: &vector<address>,
v2: &vector<address>
): vector<address> {
let len1 = vector::length(v1);
let len2 = vector::length(v2);
let result = vector::empty();
while (!vector::is_empty(&indexes)) {
let index = vector::pop_back(&mut indexes);
let chosen_vector =
if (index < len1) v1
else if (index < len2) v2
else continue; // ここで、 `continue`の型は`&vector<address>`です
vector::push_back(&mut result, *vector::borrow(chosen_vector, index))
};

result
}
}

for

このfor式は、整数型のlower_bound(包括的)式とupper_bound(非包括的)式を使用して定義された範囲を反復処理し、範囲の各要素に対してループ本体を実行します。forはループの反復回数が特定の範囲によって決定されるシナリオ向けで設計されています。

以下はforループの例で、0からn-1までの範囲の要素の合計を計算します。

script {
fun sum(n: u64): u64 {
let sum = 0;
for (i in 0..n) {
sum = sum + i;
};

sum
}
}

ループ反復子変数 (上記の例ではi)は現在数値型(境界から推論)である必要があり、境界0nここでは任意の数値式へ置き換える事が出来ます。それぞれはループの開始時に1回だけ評価されます。反復子変数ilower_bound(この場合は0)を割り当て、各ループ反復後に増分します。反復子iupper_bound(この場合はn)へ達するか超えるとループが終了します。

forループの中断続行

whileループと同様、break式をforループ内で使用して途中で終了する事が出来ます。continue式を使用して、現在の反復をスキップし、次の反復へ進む事が出来ます。以下はbreakcontinueの両方の使用例です。ループは0からn-1までの数値を反復処理し、それらを合計します。

3で割り切れる数値はスキップし(continueを使用)、10より大きい数値と遭遇すると停止します(breakを使用)。

script{
fun sum_conditional(n: u64): u64 {
let sum = 0;
for (iter in 0..n) {
if (iter > 10) {
break; // 数値が10より大きい場合はループを終了します
}
if (iter % 3 == 0) {
continue; // 数値が3で割り切れる場合は現在の反復をスキップします
}

sum = sum + iter;
};

sum
}
}

loop

loop式はbreakとヒットするまでループ本体(型()の式)をリピートします。

breakがなければ、ループは永遠に続きます。

script {
fun foo() {
let i = 0;
loop { i = i + 1 }
}
}

以下の例は、loopを使用してsum関数を記述します。

script {
fun sum(n: u64): u64 {
let sum = 0;
let i = 0;
loop {
i = i + 1;
if (i > n) break;
sum = sum + i
};

sum
}
}

予想通りcontinueloop内でも使用でき出来ます。ここではwhileの代わりにloopを使って上記sum_intermediateを書き直しました。

script {
fun sum_intermediate(n: u64): u64 {
let sum = 0;
let i = 0;
loop {
i = i + 1;
if (i % 10 == 0) continue;
if (i > n) break;
sum = sum + i
};

sum
}
}

whileloopfor式の型

Moveのループは型指定された式です。whilefor表現の型は常に()です。

script {
fun example() {
let () = while (i < 10) { i = i + 1 };
let () = for (i in 0..10) {};
}
}

loopbreakを含む場合、式の型はunit()となります。

script {
fun example() {
(loop { if (i < 10) i = i + 1 else break }: ());
let () = loop { if (i < 10) i = i + 1 else break };
}
}

loopbreakが無い場合、loopreturnabortbreakcontinue等の任意の型を持つ事が出来ます。

script {
fun example() {
(loop (): u64);
(loop (): address);
(loop (): &vector<vector<u8>>);
}
}