For Loop - HimxhMC/HimDSL GitHub Wiki

for 循环 / For Loop

基本形式 / The Basic Form

for 循环由三部分组成:初始化、条件、更新。

A for loop has three sections: init, condition, update.

for (初始化; 条件; 更新) {
    循环体;
}

执行流程:先运行初始化一次;然后检查条件,为真则执行循环体;循环体结束后执行更新;再回到条件检查。如此往复,直到条件为假。

Flow: run init once; check condition; if true, run the body; after the body, run update; back to the condition. Repeat until the condition is false.

for (int i = 0; i < 5; i = i + 1) {
    runcmd("CONSOLE", "say 第 " + i + " 次");
}

输出:

Output:

第 0 次
第 1 次
第 2 次
第 3 次
第 4 次

注意 i 从 0 开始,到 4 结束,共 5 次。i < 5 为假时退出,此时 i 是 5。

Note that i starts at 0 and ends at 4, five iterations total. The loop exits when i < 5 is false, at which point i is 5.


三部分都可以省略 / All Three Sections Are Optional

三部分都可以为空,但分号必须写全:

All three sections may be empty, but the semicolons must be present:

// 只写条件
int i = 0;
for (; i < 5; i = i + 1) { ... }

// 只写初始化
for (int i = 0; ; i = i + 1) { ... }    // 若没有 break 会死循环

// 三部分都省略
for (;;) { ... }    // 死循环,需要 break

日常脚本里很少需要省略。除非你在写"无限循环加 break"这种结构,否则三部分都写全是更好的选择。

Omitting sections is rarely needed in everyday scripts. Unless you are writing an "infinite loop with break", writing all three is clearer.


省略更新部分会怎样 / What Happens Without Update

这一条必须单独讲,因为它和你的直觉不一样。

This one deserves its own section because it defies intuition.

省略更新部分的 for 不会死循环,而是只执行一次然后退出。

A for without an update does not loop forever — it runs once and exits.

for (int i = 0; i < 10;) {
    runcmd("CONSOLE", "say " + i);
}
// 只输出 "0",然后退出,控制台会打印一条警告

HimDSL 在检测到没有更新语句时会主动打断循环,同时向控制台写一条警告。这是防止初学者写出死循环的安全设计。

HimDSL breaks the loop when it detects no update statement and writes a warning to the console. This is a safety feature against accidental infinite loops.

如果你的逻辑真的需要跳过更新(比如在循环体内部手动更新),请改用 while 循环。while 的用法在下一章。

If your logic truly needs to skip the update (say, updating inside the body), use a while loop instead. That is the next chapter.


用 for 遍历列表 / Iterating a List

最常见的用法是遍历一个列表,用索引从 0 到 size()-1:

The most common pattern: iterate from 0 to size()-1:

var players = @a;
int total = players.size();

for (int i = 0; i < total; i = i + 1) {
    var p = players.get(i);
    string name = p.name;
    runcmd("CONSOLE", "say 玩家 " + name + " 在线");
}

几点值得注意:

A few points:

  • total 只取一次,而不是在条件里写 i < players.size()。列表长度在循环过程中不会变,提前取值可以省一点开销,也更清楚地表达意图。

  • i < total 而不是 i <= total - 1,后者容易写错。

  • 循环变量 i 在循环外不可见。

  • total is read once instead of writing i < players.size() in the condition. The list length does not change during the loop; pre-reading saves a bit and expresses intent.

  • Use i < total, not i <= total - 1 — the latter is easy to get wrong.

  • The loop variable i is not visible after the loop.


复合赋值与自增 / Compound Assignment and Increment

更新部分通常写 i = i + 1,也可以写成 i++ 或 i += 1:

The update is usually i = i + 1, but i++ or i += 1 work too:

for (int i = 0; i < 5; i++) { ... }
for (int i = 0; i < 5; i += 1) { ... }
for (int i = 0; i < 5; i = i + 1) { ... }

三者等价。选哪种看个人习惯,同一个脚本里保持一致就好。

All three are equivalent. Pick one style and stay consistent within a script.


递减循环 / Counting Down

i 也可以从大往小:

i can count down as well:

for (int i = 10; i > 0; i--) {
    runcmd("CONSOLE", "say 倒计时: " + i);
}

条件 i > 0 保证不会执行到 i == 0。循环结束时 i 是 0。

i > 0 ensures the loop stops before i == 0. After the loop, i is 0.


步长不为 1 / Non-Unit Steps

不只是 i++,可以写任意更新表达式:

The update can be any expression, not only i++:

// 每次 +2,遍历偶数
for (int i = 0; i < 10; i += 2) {
    runcmd("CONSOLE", "say " + i);
}
// 0, 2, 4, 6, 8

嵌套循环 / Nested Loops

for 可以嵌套。内层循环变量名不要与外层重名:

for nests. The inner loop variable should not shadow the outer one:

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        runcmd("CONSOLE", "say (" + i + ", " + j + ")");
    }
}

上例会输出 9 行。如果要跳过内层某次,用 continue;要跳出内层,用 break(下一章)。

The above prints 9 lines. To skip one inner iteration use continue; to exit the inner loop use break (next chapter).

嵌套循环的总次数是外层次数乘以内层次数。三层以上嵌套基本意味着逻辑需要重构,考虑拆成函数。

Total iterations of nested loops = outer × inner. Three or more levels of nesting usually means the logic should be split into functions.


常见错误 / Common Mistakes

错误一:忘记写更新语句

for (int i = 0; i < 10;) { ... }    // ✗ 只跑一次

不是死循环,但也得不到想要的结果。解决方法:写 i++。

错误二:条件写成 i <= total

for (int i = 0; i <= total; i++) {
    players.get(i);    // ✗ 最后一次会越界
}

正确写法是 i < total。

错误三:在条件里改 i

for (int i = 0; i < 10; i = i + 1) { ... }    // ✓
for (int i = 0; i++ < 10; ) { ... }            // ✗ 罕见且难以维护

条件部分应当只做判断,不做修改。

Mistake 1: Missing the update

Not an infinite loop, but not what you want. Add i++.

Mistake 2: i <= total

Off-by-one; the last iteration goes out of bounds. Use i < total.

Mistake 3: Mutating i inside the condition

The condition should test, not modify.


小结 / Summary

  • for (init; condition; update)。

  • 三部分都可省略,分号必须有。

  • 省略更新部分只会执行一次,不会死循环。

  • 遍历列表用 i < list.size() 模式。

  • 更新部分可以写 i++、i += 1、i = i + 1,三者等价。

  • 嵌套循环总次数是乘积,超过三层考虑重构。

  • for (init; condition; update).

  • All three sections optional; semicolons required.

  • Omitting update runs once, not an infinite loop.

  • Iterate lists with the i < size() pattern.

  • Updates may be i++, i += 1, or i = i + 1; equivalent.

  • Nested total = product; three or more levels suggest refactoring.


下一步 / Next Steps

下一章 [while 与 do-while / While-and-Do-While] 讲另外两种循环,特别是当循环次数不确定时的用法。

Next, [while 与 do-while / While-and-Do-While] covers the other two loops, especially when the iteration count is not known in advance.