如何在循环体中编写条件
How to write conditions in loop bodies?
这可能有点主观,但实际上我正在寻找包含一些推理的答案。
我已经满足了循环体中条件的以下两种编程风格。:
for (int i = 0; i < myArray.length; i++) {
if (myArray[i].isEmpty())
continue;
doSomeStuff();
doSomeMoreStuff();
}
:
for (int i = 0; i < myArray.length; i++) {
if (!myArray[i].isEmpty()) {
doSomeStuff();
doSomeMoreStuff();
}
}
我通常使用第一种样式,因为它保持缩进级别一致,特别是当有多个条件时。但我开始怀疑,第二种是不是真的更干净。你有喜欢的风格吗?你能解释一下为什么吗?
更新:
这里有一个更现实的例子。假设我正在阅读这样一个文件"名:姓",例如:
John;Doe
Joe;Bloggs
这就是我如何将每行读入一个name对象,忽略空行(可能会出现):
while (line = file.readLine()) {
if (line.isEmpty())
continue;
String[] columns = line.split(";");
names.add(new Name(columns[0], columns[1]));
}
到目前为止,我更喜欢第二种,因为我发现它在语义上更容易阅读。这使得重构更容易。
事实上,第二种方法增加了圈复杂度,并可能导致箭头反模式。
此外,在开始时保留所有"代码不应通过此点"检查允许您将它们分组在一起,这意味着它们更容易维护。
这在很大程度上取决于实际代码的样子;如果它本质上是顺序的,我更喜欢初始的continue
如果满足条件,如果它是分支的,我用分支来跳过迭代;然而,如果嵌套的关卡变得很深,我可能会使用continue
…如果你想要一个单一的样式,并且总是在每个循环中使用它,我建议你使用第二个版本(分支),但是,因为它比continue
更少情境化。
它通常取决于条件。我尽量避免否定条件,尤其是更复杂的条件。因此,我经常以第一种风格出来。一开始就把特殊情况整理出来,这样就可以一次写出一般情况下的算法。
我使用continue
(有时在循环中间使用break
或goto
),但总是将其全部放在一行(因此continue
不会意外地与if
分开),并始终提供形式为"if…,我们完成了"的注释。
注释是一个强大的工具。十层缩进确实比goto
更难读。有些控制流结构本身不会记录下来。
只管重构。原因是如果MyObject是空的,那是一个内部调用它可以问自己。没有理由让其他人问MyObject是否为空,只是为了让MyObject做一些事情。这是属于MyObject的逻辑。你想把逻辑尽可能地放在对象内部,这样它就可以被其他潜在的调用者重用,而且如果你可以避免的话,系统的其他部分也不会调用它们没有所有权的东西。
for(MyObject object : list) {
object.doABunchOfSimilarThings();
}
....
class MyObject {
...
public void doABunchOfSimilarThings() {
if(notEmpty()) {
doThing1();
doThing2();
doThing3();
}
}
...
}
对我来说,这取决于循环的内容。
如果循环只包含最多5行和一个条件,我通常会使用第二种风格,因为我发现它更容易看到代码将在何时执行。
如果有很多嵌套条件(没有else
),我更喜欢第一种语法,原因和你一样:防止大的缩进。当使用第二种样式时,每个后续条件检查都不必要地增加缩进级别,从而降低可读性。
continue, goto, break是我们最不该用的词。如果方法再长一点,你就会发现这是个坏主意。第二个教训是尽可能使用肯定条件:)
免责声明:本文中的答案仅反映了回答者的想法,不应被视为普遍共识
对我来说,这是关于可读性。虽然我可以理解这两个循环体,但有些人可能需要时间来理解它们。
第二个循环体只在数组中的元素不为空时执行,而第一个循环体发现数组中的元素为空并通知for循环继续。
第二个循环的可读性也使有意识的调试变得容易,就好像它告诉一个人,如果调用doSomeStuff()
或doSomeMoreStuff()
,元素永远不能为空。
第二个更好。在编程时尽量避免使用continue、break和goto(猛禽?)他们只是拐杖。
第二个。如果有很多缩进级别,可以通过将块转换为单独的方法来进行重构。
怎么样
for (int i = 0; i < myArray.length; i++) {
doStuffToValue(myArray[i]);
}
void doStuffToValue(String value) {
if (value.isEmpty()) {
return;
}
doSomeStuff();
doSomeMoreStuff();
}
这样就使检查值的有效性成为方法的关注点,而不是循环体的关注点。如果需要,您甚至可以为doStuffToValue方法编写一个单元测试。
我认为我从来没有在循环中使用过continue
,尽管我经常在函数的开头使用return
。从风格上讲,部分原因是continue
只适用于需要跳过整个循环剩余部分的上下文中,而if
语句则适用于任何上下文中。因此,if
倾向于与代码的其他部分更加一致。
一个不太主观的原因是,如果你经常跳过整个循环迭代,通常有一种方法可以在循环之外重构决策,这对性能有影响。对于您的示例,我更愿意使用永远不会有空值的数据结构。
对于性能不是问题的不可避免的复杂事情,我倾向于使用Adriaan的方法将循环体分解为函数。
在这三个原因之间,continue
在语法上是可能的,最容易阅读,与使用该数据结构的其他代码一致,以及最高性能的解决方案的情况并不经常出现,不足以证明它的使用。
- 如何在for循环中包含两个索引值的测试条件
- 如果条件为TRUE(最佳方式?),则在do while循环中后置增量
- 循环中的条件:为什么每次都调用strlen(),而vector.size()只调用一次
- 即使没有满足他们的条件,我也无法通过一些 do-while 循环
- 在循环条件 i<sqrtN(预先计算)和 i*i<N 中,C++哪个更有效?
- 尽管条件为真,循环仍停止
- 为什么我不能在 for 循环中出现这样的条件?(C++)
- C++ 即使不满足条件,循环也会终止
- 具有迭代器和自定义步长的循环结束条件
- 有没有办法在循环中的条件至少满足一次时运行语句?
- 为什么循环没有中断,并且 if 条件没有按预期工作?
- 为什么我不能将 size() 函数编写为 while 循环中的条件?
- 虽然循环即使应用了错误条件也不会退出
- 当循环在条件C++之前停止工作时
- Leetcode C++用于循环条件
- 当满足条件时,While循环未结束
- 对于检查 >=0 终止条件时的循环索引类型
- 调用以条件循环(c++)
- 使用布尔条件c++循环时无法退出
- 在条件C++循环语句中向SQLite3表中插入条目