我正在阅读别人的代码,他们在循环内部单独增加了for循环计数器,并且包含了通常的后续操作。例如:
for( int y = 4; y < 12; y++ ) {
// blah
if( var < othervar ) {
y++;
}
// blah
}
根据大多数人编写和阅读的代码,我应该期望看到这个吗?
我正在阅读别人的代码,他们在循环内部单独增加了for循环计数器,并且包含了通常的后续操作。例如:
for( int y = 4; y < 12; y++ ) {
// blah
if( var < othervar ) {
y++;
}
// blah
}
根据大多数人编写和阅读的代码,我应该期望看到这个吗?
for(something)
if (somethingElse)
y++
遵循这个原则需要您将那个 if
块移入自己的方法中,这会使得在该方法中操作一些外部计数器变得笨拙。
但除此之外,可能会出现类似您示例的情况;但对于这些情况,为什么不使用 while 循环呢?
换句话说:使您的示例复杂和混乱的是代码中的两个不同部分更改了您的循环计数器。因此,另一种方法可能如下:
while (y < whatever) {
...
y = determineY(y, who, knows);
}
那种新方法可以成为一个核心地方,来确定如何更新循环变量。
y++
可以实现这一点。当然,这取决于上下文并且可能需要有明确的注释来解释正在发生的事情,但是我没有看到更好的替代方案,因为它们都需要明确的注释... - Bakuriu我不同意上面所赞同的答案。在循环体内部操纵循环控制变量是没有问题的。例如,下面是清理地图的经典示例:
for (auto it = map.begin(), e = map.end(); it != e; ) {
if (it->second == 10)
it = map.erase(it);
else
++it;
}
由于有人正确地指出迭代器并不等同于数值控制变量,因此让我们考虑一下解析字符串的示例。假设该字符串由一系列字符组成,其中以 '\' 为前缀的字符被视为特殊字符,需要跳过:
for (size_t i = 0; i < s_len; ++i) {
if (s[i] == '\\') {
++i;
continue;
}
process_symbol(s[i]);
}
y=4
开始呢?为了搞混读者吗?循环的目的是迭代一系列(大多数情况下)连续的数字,并为每个值执行某些操作。有时您需要跳过一个值(添加 y++
将完美地解决这个问题)。 - Bakuriu$
是一个特殊符号,\$
也不是特殊的。 process_symbol
每次只会给出一个字符,因此无法看到 $
是由 \
预先处理的。 - user743382it
的可见性可能是一个原因。 - SergeyA使用 while 循环替代。
虽然你可以用 for 循环完成这个任务,但你不应该这样做。记住,程序就像其他任何沟通的工具一样,必须考虑到受众。对于程序,受众包括编译器和下一个要维护代码的人(可能是六个月后的你)。
对于编译器而言,代码被非常字面地执行——设置索引变量、运行循环体、执行增量,然后检查条件以查看是否需要再次循环。编译器并不关心你是否疯狂更改循环索引。
然而,对于人来说,for 循环有一个特定的含义:固定次数地运行此循环。如果你疯狂更改循环索引,那么这就违反了这种含义。从某种意义上说,这是不诚实的,也很重要,因为下一个阅读代码的人要么必须花费额外的精力来理解循环,要么将无法理解。
如果你想疯狂更改循环索引,请使用 while 循环。特别是在 C/C++/相关语言中,for 循环和 while 循环的功能完全相同,因此你不会失去任何功能或表达能力。任何 for 循环都可以转换为 while 循环,反之亦然。但是,下一个阅读代码的人不会依赖于你不疯狂更改循环索引的含义。将其变成 while 循环而不是 for 循环是一个警告,表示这种循环可能更加复杂,在你的情况下,确实更加复杂。
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}
在这里,end()
迭代器的非常数性质以及循环内的增量都是令人惊讶的,因此需要进行记录。注意:此处循环提升最终可能应该为了代码清晰度而完成。
end
迭代器没有失效,所以你不需要在每次迭代时调用它。 - SergeyA值得一提的是,以下是C++核心准则对此主题的建议:
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-loop-counter
ES.86: 避免在原生for循环的循环体内修改循环控制变量
原因:循环控制应该在前面,以使我们正确地推断出循环内发生了什么。在迭代表达式和循环体内修改循环计数器是引起意外和错误的固有方法。
同时,还要注意其他回答中讨论使用std::map
的情况。在这种情况下,控制变量的增量仍然只会在每次迭代中执行一次,而在您的示例中,它可以在每次迭代中执行多次。
最好使用while循环来编写
y = 4;
while(y < 12)
{
/* body */
if(condition)
y++;
y++;
}
while(y < 12)
{
/* body */
y += condition ? 2 : 1;
}
如果您很少跳过项目,比如在带引号的字符串中使用转义符号,我会允许使用for()方法。