Skip to content
🎉 Welcome to the new Aptos Docs! Click here to submit an issue.
构建Smart Contracts (Move)Move Book | Move 书籍While, For, and Loop | 循环语句

While、For 和 Loop 循环

Move 提供了三种循环结构:whileforloop

while 循环

while 结构会重复执行循环体(一个 unit 类型表达式),直到条件(一个 bool 类型表达式)求值为 false

以下是计算从 1n 数字总和的简单 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 表达式跳过循环剩余部分,继续下一次迭代。这个循环使用 continue 来计算 1, 2, ..., n 的总和,但跳过能被 10 整除的数字:

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 适用于循环次数由特定区间决定的场景。

以下是计算从 0n-1 区间元素总和的 for 循环示例:

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

循环迭代变量(上例中的 i)当前必须是数值类型(根据边界自动推断),这里的边界 0n 可以被任意数值表达式替代。每个边界仅在循环开始时计算一次。迭代变量 i 会被赋值为 lower_bound(本例中为 0)并在每次循环迭代后递增;当迭代器 i 达到或超过 upper_bound(本例中为 n)时循环终止。

for 循环中的 breakcontinue

while 循环类似,for 循环中可以使用 break 表达式提前退出循环,continue 表达式可跳过当前迭代进入下一次循环。以下示例演示了二者的用法:该循环会遍历 0n-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
  }
}

如你所料,continue 也可用于 loop 内部。以下是之前用 while 实现的 sum_intermediate 改用 loop 的版本:

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) {};
  }
}

如果 loop 包含 break,则该表达式具有单元类型 ()

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

如果 loop 不包含 break,则 loop 可以具有任意类型,这与 returnabortbreakcontinue 类似。

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

循环标签

自语言版本 2.1 起

whileloop 语句可以拥有一个标签,该标签可以被 breakcontinue 语句引用。在存在嵌套循环的情况下,这允许引用外部循环。例如:

script {
  fun example(x: u64): u64 {
    'label1: while (x > 10) {
      loop {
        if (x % 2 == 0) {
          x -= 1;
          continue 'label1;
        } else if (x < 10) {
          break 'label1
        } else
          x -= 2
      }
    };
    x
  }
}