for 循环参数是否执行每次迭代?

Does for loop parameters execute each iteration?

在某些时候,我想在创建数组时随机化数组是否会反转。

我想到的第一件事是:

bool reverse = Random.value > 0.5f;
for (
        int i = reverse ? steps - 1 : 0; 
        reverse ? i >= 0 : i < steps; 
        i += reverse ? -1 : +1
    ) {
    rotateScript.angles.Add(angleRange[0] + stepAdder * i);
}

后来我意识到,尽管需要更多行,但 if/else 中的两个 for 循环会更具可读性。

但问题仍然存在:这些三元条件是每次迭代都执行一次,还是只执行一次?

正确,for 语句的第 2 和第 3 个组件在每次迭代中都被评估。

这个:

for( x; y; z ) a;

相当于:

x;
while( y ) { a; z; }

因此 y 将比 z 多评估 1 次。

正如@Dai 指出的那样,必须在每次迭代结束时评估第二个和第三个语句。

此行为在 C# 语言规范的 §8.8.3 中有详细说明。换句话说,它说的是

for ( initialiser; condition; iterator; ) { ... }

声明:

A for statement is executed as follows:

  • If a for-initializer is present, the variable initializers or statement expressions are executed in the order they are written. This step is only performed once.
  • If a for-condition is present, it is evaluated.
  • If the for-condition is not present or if the evaluation yields true, control is transferred to the embedded statement. When and if control reaches the end point of the embedded statement, the expressions of the for-iterator, are evaluated in sequence, and then another iteration is performed, starting with evaluation of the for-condition in the step above.
  • If the for-condition is present and the evaluation yields false, control is transferred to the end point of the for statement.

用你的代码

for (
    int i = reverse ? steps - 1 : 0; 
    reverse ? i >= 0 : i < steps; 
    i += reverse ? -1 : +1
) {
    rotateScript.angles.Add(angleRange[0] + stepAdder * i);
}

您确实有一些选项可以使到达迭代时的三元运算符变得不必要。

您提到使用带有两个 for 循环的 if/else,但我建议您不要这样做,因为它会重复代码,这会阻碍其可维护性和可读性。


选项 1:使用标准 for 循环:

您可以将逻辑放入循环中的代码中,即

for ( i = 0; i < steps; i += 1 )
{
    int multiplier = reverse ? steps - 1 - i: i;
    rotateScript.angles.Add(angleRange[0] + stepAdder * multiplier);
}

方案二:将三元逻辑移出:

先计算极限和增量,即

int start = reverse ? steps - 1 : 0;
int increment = reverse ? -1 : +1;
int limit = reverse ? -1 : steps;

for ( int i = start; i != limit; i += increment; )
{
    rotateScript.angles.Add(angleRange[0] + stepAdder * i);
}

我更喜欢选项 1,因为我认为大多数人都觉得直截了当的 for 循环更容易理解。