I almost never see a for
loop like this:
我几乎从未见过这样的for循环:
for (int i = 0; 5 != i; ++i)
{}
Is there a technical reason to use >
or <
instead of !=
when incrementing by 1 in a for
loop? Or this is more of a convention?
在for循环中增加1时,使用>或 <而不是!=是否有技术原因?或者这更像是一种惯例?< p>
21 个解决方案
#1
292
while (time != 6:30pm) {
Work();
}
It is 6:31pm... Damn, now my next chance to go home is tomorrow! :)
6:31pm……该死,现在我回家的下一个机会是明天!:)
This to show that the stronger restriction mitigates risks and is probably more intuitive to understand.
这表明,更严格的限制可以降低风险,而且可能更容易理解。
#2
93
There is no technical reason. But there is mitigation of risk, maintainability and better understanding of code.
没有技术原因。但是降低了风险、可维护性和更好地理解代码。
<
or >
are stronger restrictions than !=
and fulfill the exact same purpose in most cases (I'd even say in all practical cases).
<或> 的限制比!=更严格,并且在大多数情况下(我甚至可以说在所有实际情况下)都实现了完全相同的目的。
There is duplicate question here; and one interesting answer.
这里有重复的问题;和一个有趣的答案。
#3
82
There are already answers, but there is another case, where the difference matters. If you have a loop like this:
已经有答案了,但还有另一种情况,差别很重要。如果你有这样一个循环:
for (int i = a; i < b; i++){}
this loop will work as expected for any values of a
and b
while if you used !=
you would have to check for a <= b
before running the loop.
这个循环对于a和b的任何值都将像预期的那样工作,如果您使用!=,那么您必须在运行循环之前检查a <= b。
So again it boils down to: In most cases there is no difference, but in some cases the small difference matters a lot. If you want to have all your loops look the same (and not for each loop have to consider which version is better) then just use <
and not !=
.
所以它可以归结为:在大多数情况下没有区别,但在某些情况下,小的差别是很重要的。如果你想拥有你所有的循环看起来一样(而不是为每个循环必须考虑哪个版本更好)然后用 <不! =。< p>
In some special cases you might need !=
and not <
, but these are really special cases and then it makes sense that they look different than the usual loops.
在某些特殊情况下,您可能需要!=而不是<,但这些是非常特殊的情况,因此它们看起来与通常的循环不一样。
When I first answered this question I considered only plain old loops with an int
counter. Once iterators enter the game, the situation is different. Loops with iterators usually look like this:
当我第一次回答这个问题时,我只考虑使用int计数器的普通旧循环。一旦迭代器进入游戏,情况就不同了。带有迭代器的循环通常是这样的:
for (Iterator it = v.begin(); it != v.end(); ++it);
The reason is* that it is a much weaker constraint to implement an !=
operator. Almost any two object can be tested for inequality, but not for all types a <
operator makes sense. Especially when the containers elements are not ordered the iterators cannot implement it. Thus the more generic way of writing such a loop is with !=
.
原因是,实现一个!=操作符的约束要弱得多。几乎任何两个对象都可以被测试为不平等,但对于所有类型的 <操作符都是有意义的。特别是当容器元素没有被排序时,迭代器无法实现它。因此,编写这样一个循环的通用方法是用!=。< p>
However, my point from above is still valid: There might be cases with an int
counter where you have to use !=
. They are rare and when they occur you want them to be easily identifiable. Thus it is common to write most int
-counter loops with <
.
但是,我上面的观点仍然是有效的:可能存在需要使用int计数器的情况!=。它们是罕见的,当它们出现时,你希望它们易于识别。因此,使用 <编写大多数的intcounter循环是很常见的。< p>
* = Comes without warranty. Please someone correct me if I am wrong ;)
没有担保就来了。如果我说错了,请指正。
#4
65
You can have something like
你可以有类似的东西
for(int i = 0; i<5; ++i){
...
if(...) i++;
...
}
If your loop variable is written by the inner code, the i!=5
might not break that loop. This is safer to check for inequality.
如果循环变量是由内部代码i编写的!=5可能不会破坏循环。检查不平等更安全。
Edit about readability. The inequality form is way more frequently used. Therefore, this is very fast to read as there is nothing special to understand (brain load is reduced because the task is common). So it's cool for the readers to make use of these habits.
编辑对可读性。不等式形式更常用。因此,这是非常快的阅读,因为没有什么特别的需要去理解(大脑负荷减少,因为任务是常见的)。所以读者利用这些习惯是很酷的。
#5
47
And last but not least, this is called defensive programming, meaning to always take the strongest case to avoid current and future errors influencing the program.
最后但并非最不重要的是,这被称为防御性编程,意思是始终采取最强的情况,以避免当前和未来的错误影响程序。
The only case where defensive programming is not needed is where states have been proven by pre- and post-conditions (but then, proving this is the most defensive of all programming).
唯一不需要防御性编程的情况是,状态已经被前置和后置条件所证明(然后,证明这是所有编程中最具防御性的)。
#6
37
I would argue that an expression like
我认为这样的表达方式
for ( int i = 0 ; i < 100 ; ++i )
{
...
}
is more expressive of intent than is
意图的表达比实际要多吗
for ( int i = 0 ; i != 100 ; ++i )
{
...
}
The former clearly calls out that the condition is a test for an exclusive upper bound on a range; the latter is a binary test of an exit condition. And if the body of the loop is non-trivial, it may not apparent that the index is only modified in the for
statement itself.
前者明确指出,条件是对范围上的唯一上限的测试;后者是出口条件的二进制测试。而且,如果循环的主体不是平凡的,那么索引可能不会明显地只在for语句本身中修改。
#7
20
Iterators are an important case when you most often use the !=
notation:
当您最经常使用!=符号时,迭代器是一个重要的例子:
for(auto it = vector.begin(); it != vector.end(); ++it) {
// do stuff
}
Granted: in practice I would write the same relying on a range-for
:
当然,在实践中,我也会根据范围写同样的东西——因为:
for(auto & item : vector) {
// do stuff
}
but the point remains: one normally compares iterators using ==
or !=
.
但重点仍然是:通常使用==或!=来比较迭代器。
#8
17
The loop condition is an enforced loop invariant.
循环条件是一个强制循环不变量。
Suppose you don't look at the body of the loop:
假设你不看循环的主体:
for (int i = 0; i != 5; ++i)
{
// ?
}
in this case, you know at the start of the loop iteration that i
does not equal 5
.
在这种情况下,您知道在循环迭代的开始,我不等于5。
for (int i = 0; i < 5; ++i)
{
// ?
}
in this case, you know at the start of the loop iteration that i
is less than 5
.
在这种情况下,你知道在循环迭代的开始i小于5。
The second is much, much more information than the first, no? Now, the programmer intent is (almost certainly) the same, but if you are looking for bugs, having confidence from reading a line of code is a good thing. And the second enforces that invariant, which means some bugs that would bite you in the first case just cannot happen (or don't cause memory corruption, say) in the second case.
第二个比第一个要多得多,不是吗?现在,程序员的意图(几乎是肯定的)是一样的,但是如果您正在寻找bug,那么阅读一行代码就有信心是一件好事。第二个是不变的,这意味着在第一个例子中会咬到你的一些错误在第二种情况下不会发生(或者不会导致内存损坏)。
You know more about the state of the program, from reading less code, with <
than with !=
. And on modern CPUs, they take the same amount of time as no difference.
您可以通过读取更少的代码,使用 <而不是!=来了解程序的状态。而在现代cpu上,它们所花费的时间是一样的。< p>
If your i
was not manipulated in the loop body, and it was always increased by 1, and it started less than 5
, there would be no difference. But in order to know if it was manipulated, you'd have to confirm each of these facts.
如果你的i在循环体中没有被操纵,它总是增加1,并且开始时小于5,那么没有区别。但是为了知道它是否被操纵,你必须确认每一个事实。
Some of these facts are relatively easy, but you can get wrong. Checking the entire body of the loop is, however, a pain.
有些事实相对容易,但你可能会出错。然而,检查整个回路是一种痛苦。
In C++ you can write an indexes
type such that:
在c++中,可以编写这样的索引类型:
for( const int i : indexes(0, 5) )
{
// ?
}
does the same thing as either of the two above for
loops, even down to the compiler optimizing it down to the same code. Here, however, you know that i
cannot be manipulated in the body of the loop, as it is declared const
, without the code corrupting memory.
做与上面两个循环中的任何一个一样的事情,甚至到编译器优化它到相同的代码。但是,在这里,您知道我不能在循环的主体中操作,因为它声明为const,而代码不会损坏内存。
The more information you can get out of a line of code without having to understand the context, the easier it is to track down what is going wrong. <
in the case of integer loops gives you more information about the state of the code at that line than !=
does.
在不需要理解上下文的情况下,从一行代码中获得的信息越多,就越容易跟踪出错的地方。 <在整型循环的情况下,与!=相比,提供了更多关于该行代码状态的信息。< p>
#9
12
As already said by Ian Newson, you can't reliably loop over a floating variable and exit with !=
. For instance,
正如Ian Newson所说,您不能可靠地循环一个浮动变量并使用!=退出。例如,
for (double x=0; x!=1; x+=0.1) {}
will actually loop forever, because 0.1 can't exactly be represented in floating point, hence the counter narrowly misses 1. With <
it terminates.
实际上会一直循环下去,因为0。1不能准确地用浮点数表示,所以计数器仅差1。 <它终止。< p>
(Note however that it's basically undefined behaviour whether you get 0.9999... as the last accepted number – which kind of violates the less-than assumption – or already exit at 1.0000000000000001.)
(但请注意,无论你是否得到0.9999,它基本上都是未定义的行为……)作为最后一个被接受的数字——这有点违反了小于假设——或者已经退出了1.0000000000000000000000000001)。
#10
12
It may happen that the variable i
is set to some large value and if you just use the !=
operator you will end up in an endless loop.
变量i可能被设置为某个大值,如果您只是使用!=操作符,那么您将会得到一个无穷无尽的循环。
#11
11
As you can see from the other numerous answers, there are reasons to use < instead of != which will help in edge cases, initial conditions, unintended loop counter modification, etc...
正如您可以从其他众多答案中看到的,使用 <而不是!=将有助于边的情况、初始条件、意外的循环计数器修改等等……< p>
Honestly though, I don't think you can stress the importance of convention enough. For this example it will be easy enough for other programmers to see what you are trying to do, but it will cause a double-take. One of the jobs while programming is making it as readable and familiar to everyone as possible, so inevitably when someone has to update/change your code, it doesn't take a lot of effort to figure out what you were doing in different code blocks. If I saw someone use !=
, I'd assume there was a reason they used it instead of <
and if it was a large loop I'd look through the whole thing trying to figure out what you did that made that necessary... and that's wasted time.
老实说,我认为你不能强调公约的重要性。对于这个示例,其他程序员很容易看到您正在尝试做什么,但这将导致重复。编程的工作之一就是让每个人都能尽可能地阅读和熟悉它,所以当有人必须更新/更改代码时,不可避免地,不需要花费很多精力去弄清楚您在不同的代码块中做了什么。如果我看到有人使用!=,我想他们使用它而不是 <是有原因的,如果是一个大的循环,我会仔细检查整个过程,试图弄清楚你做了什么,这是必要的……这是浪费时间。< p>
#12
9
There are several ways to write any kind of code (usually), there just happens to be two ways in this case (three if you count <= and >=).
有几种方法可以编写任何类型的代码(通常),在这种情况下只有两种方法(如果计数为<=和>=,则为三种)。
In this case, people prefer > and < to make sure that even if something unexpected happens in the loop (like a bug), it won't loop infinitely (BAD). Consider the following code, for example.
在这种情况下,人们更喜欢>和<,以确保即使在循环中发生了意外的事情(比如bug),它也不会无限循环(糟糕)。例如,考虑下面的代码。
for (int i = 1; i != 3; i++) {
//More Code
i = 5; //OOPS! MISTAKE!
//More Code
}
If we used (i < 3), we would be safe from an infinite loop because it placed a bigger restriction.
如果我们使用(i < 3),我们就可以避免无限循环,因为它有更大的限制。
Its really your choice whether you want a mistake in your program to shut the whole thing down or keep functioning with the bug there.
无论您是希望程序中出现错误,关闭整个程序,还是继续运行bug,这都是您的选择。
Hope this helped!
希望这帮助!
#13
9
I take the adjectival "technical" to mean language behavior/quirks and compiler side effects such as performance of generated code.
我用形容词“技术性”表示语言行为/怪癖和编译器副作用,如生成代码的性能。
To this end, the answer is: no(*). The (*) is "please consult your processor manual". If you are working with some edge-case RISC or FPGA system, you may need to check what instructions are generated and what they cost. But if you're using pretty much any conventional modern architecture, then there is no significant processor level difference in cost between lt
, eq
, ne
and gt
.
为此,答案是:没有(*)。(*)是“请参考您的处理器手册”。如果您正在使用一些edge-case RISC或FPGA系统,您可能需要检查生成的指令和它们的成本。但是,如果您使用的是几乎任何传统的现代架构,那么lt、eq、ne和gt之间在处理器级别上的成本上没有显著差异。
If you are using an edge case you could find that !=
requires three operations (cmp
, not
, beq
) vs two (cmp
, blt xtr myo
). Again, RTM in that case.
如果您使用的是一个边缘案例,您可以发现!=需要三个操作(cmp,不是,beq) vs . 2 (cmp, blt xtr myo)。在这种情况下,RTM。
For the most part, the reasons are defensive/hardening, especially when working with pointers or complex loops. Consider
在大多数情况下,原因是防御/强化,特别是在处理指针或复杂循环时。考虑
// highly contrived example
size_t count_chars(char c, const char* str, size_t len) {
size_t count = 0;
bool quoted = false;
const char* p = str;
while (p != str + len) {
if (*p == '"') {
quote = !quote;
++p;
}
if (*(p++) == c && !quoted)
++count;
}
return count;
}
A less contrived example would be where you are using return values to perform increments, accepting data from a user:
一个不那么做作的例子是,您使用返回值执行增量,接受来自用户的数据:
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i != len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
i += step; // here for emphasis, it could go in the for(;;)
}
}
Try this and input the values 1, 2, 10, 999.
试试这个,输入值1,2,10,999。
You could prevent this:
你可以防止这种情况:
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i != len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
if (step + i > len)
std::cout << "too much.\n";
else
i += step;
}
}
But what you probably wanted was
但你可能想要的是
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i < len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
i += step;
}
}
There is also something of a convention bias towards <
, because ordering in standard containers often relies on operator<
, for instance hashing in several STL containers determines equality by saying
对于 <也存在某种约定偏差,因为在标准容器中排序通常依赖于操作符<,例如,在几个stl容器中的散列通过声明确定相等性< p>
if (lhs < rhs) // T.operator <
lessthan
else if (rhs < lhs) // T.operator < again
greaterthan
else
equal
If lhs
and rhs
are a user defined class writing this code as
如果lhs和rhs是用户定义的类,则将此代码编写为
if (lhs < rhs) // requires T.operator<
lessthan
else if (lhs > rhs) // requires T.operator>
greaterthan
else
equal
The implementor has to provide two comparison functions. So <
has become the favored operator.
实现者必须提供两个比较函数。所以 <已成为受欢迎的操作符。< p>
#14
9
Yes; OpenMP doesn't parallelize loops with the !=
condition.
是的,OpenMP不会将循环与!=条件并行化。
#15
7
The most common reason to use <
is convention. More programmers think of loops like this as "while the index is in range" rather than "until the index reaches the end." There's value is sticking to convention when you can.
使用 <的最常见原因是约定。更多的程序员认为这样的循环是“当索引在范围内”,而不是“直到索引到达末尾”。只要有可能,坚持惯例是有价值的。< p>
On the other hand, many answers here are claiming that using the <
form helps avoid bugs. I'd argue that in many cases this just helps hide bugs. If the loop index is supposed to reach the end value, and, instead, it actually goes beyond it, then there's something happening you didn't expect which may cause a malfunction (or be a side effect of another bug). The <
will likely delay discovery of the bug. The !=
is more likely to lead to a stall, hang, or even a crash, which will help you spot the bug sooner. The sooner a bug is found, the cheaper it is to fix.
另一方面,这里的许多答案声称使用< form有助于避免错误。我认为在很多情况下,这只会帮助隐藏bug。如果循环索引应该达到最终值,而实际上它超出了这个值,那么会发生一些您没有预料到的事情,可能会导致故障(或者是另一个bug的副作用)。 <可能会延迟发现错误。更有可能导致失速、挂起,甚至崩溃,这将帮助您更快地发现bug。bug越早被发现,修复起来就越便宜。< p>
Note that this convention is peculiar to array and vector indexing. When traversing nearly any other type of data structure, you'd use an iterator (or pointer) and check directly for an end value. In those cases you have to be sure the iterator will reach and not overshoot the actual end value.
注意,这种约定是数组和向量索引特有的。当遍历几乎任何其他类型的数据结构时,您将使用迭代器(或指针)并直接检查最终值。在这些情况下,您必须确保迭代器将达到并不会超过实际的最终值。
For example, if you're stepping through a plain C string, it's generally more common to write:
例如,如果您正在处理一个普通的C字符串,那么通常写:
for (char *p = foo; *p != '\0'; ++p) {
// do something with *p
}
than
比
int length = strlen(foo);
for (int i = 0; i < length; ++i) {
// do something with foo[i]
}
For one thing, if the string is very long, the second form will be slower because the strlen
is another pass through the string.
首先,如果字符串很长,第二种形式会比较慢,因为strlen是另一种通过字符串的方式。
With a C++ std::string, you'd use a range-based for loop, a standard algorithm, or iterators, even if though the length is readily available. If you're using iterators, the convention is to use !=
rather than <
, as in:
使用c++ std::string,您将使用基于范围的for循环、标准算法或迭代器,即使长度很容易获得。如果您使用的是迭代器,则该约定是使用!=而不是<,如:
for (auto it = foo.begin(); it != foo.end(); ++it) { ... }
Similarly, iterating a tree or a list or a deque usually involves watching for a null pointer or other sentinel rather than checking if an index remains within a range.
类似地,迭代树、列表或deque通常需要查看空指针或其他标记,而不是检查索引是否仍然在范围内。
#16
7
One reason not to use this construct is floating point numbers. !=
is a very dangerous comparison to use with floats as it'll rarely evaluate to true even if the numbers look the same. <
or >
removes this risk.
不使用此结构的一个原因是浮点数。!=是一个非常危险的比较,使用浮动,因为它很少会评估为真,即使数字看起来是一样的。 <或> 消除了这种风险。
#17
7
There are two related reasons for following this practice that both have to do with the fact that a programming language is, after all, a language that will be read by humans (among others).
遵循这种实践有两个相关的原因,这两个原因都与编程语言毕竟是一种供人类阅读的语言这一事实有关。
(1) A bit of redundancy. In natural language we usually provide more information than is strictly necessary, much like an error correcting code. Here the extra information is that the loop variable i
(see how I used redundancy here? If you didn't know what 'loop variable' means, or if you forgot the name of the variable, after reading "loop variable i
" you have the full information) is less than 5 during the loop, not just different from 5. Redundancy enhances readability.
(1)有一点冗余。在自然语言中,我们通常提供的信息比完全必要的要多,就像错误校正代码一样。这里的额外信息是循环变量i(看到我如何使用冗余了吗?如果你不知道“循环变量”是什么意思,或者如果你忘记了变量的名字,在读取“循环变量i”之后,你会得到完整的信息)在循环期间小于5,而不仅仅是与5不同。冗余提高可读性。
(2) Convention. Languages have specific standard ways of expressing certain situations. If you don't follow the established way of saying something, you will still be understood, but the effort for the recipient of your message is greater because certain optimisations won't work. Example:
(2)约定。语言有特定的标准表达方式。如果你不遵循既定的表达方式,你仍然会被理解,但是对你的信息接收者的努力更大,因为某些优化不会起作用。例子:
Don't talk around the hot mash. Just illuminate the difficulty!
不要谈论热土豆泥。只是照亮了困难!
The first sentence is a literal translation of a German idiom. The second is a common English idiom with the main words replaced by synonyms. The result is comprehensible but takes a lot longer to understand than this:
第一句是德语习语的直译。第二种是常用的英语成语,主要用同义词代替。结果是可以理解的,但需要更长的时间来理解:
Don't beat around the bush. Just explain the problem!
别拐弯抹角了。只是解释这个问题!
This is true even in case the synonyms used in the first version happen to fit the situation better than the conventional words in the English idiom. Similar forces are in effect when programmers read code. This is also why 5 != i
and 5 > i
are weird ways of putting it unless you are working in an environment in which it is standard to swap the more normal i != 5
and i < 5
in this way. Such dialect communities do exist, probably because consistency makes it easier to remember to write 5 == i
instead of the natural but error prone i == 5
.
这是正确的,即使在第一个版本中使用的同义词恰好比英语习语中的常规词更适合这种情况。当程序员阅读代码时,同样的力量也会发挥作用。这也是为什么5 !这样的方言社区确实存在,可能是因为一致性使人们更容易记住写5 = i,而不是写自然但容易出错的i = 5。
#18
5
Using relational comparisons in such cases is more of a popular habit than anything else. It gained its popularity back in the times when such conceptual considerations as iterator categories and their comparability were not considered high priority.
在这种情况下,使用关系比较更像是一种流行的习惯。当迭代器类别和它们的可比性没有被认为是高优先级时,它在时代中得到了它的欢迎。
I'd say that one should prefer to use equality comparisons instead of relational comparisons whenever possible, since equality comparisons impose less requirements on the values being compared. Being EqualityComparable is a lesser requirement than being LessThanComparable.
我想说的是,人们应该尽可能地使用相等性比较而不是关系比较,因为相等性比较对所比较的值的要求较少。平等可比性比不可比性要求要低。
Another example that demonstrates the wider applicability of equality comparison in such contexts is the popular conundrum with implementing unsigned
iteration down to 0
. It can be done as
另一个例子证明了平等比较在这种上下文中更广泛的适用性,那就是将无符号迭代实现为0的普遍难题。可以这样做
for (unsigned i = 42; i != -1; --i)
...
Note that the above is equally applicable to both signed and unsigned iteration, while the relational version breaks down with unsigned types.
注意,上面的内容同样适用于有符号和无符号迭代,而关系版本则使用无符号类型进行分解。
#19
2
Besides the examples, where the loop variable will (unintentional) change inside the body, there are other reasions to use the smaller-than or greater-than operators:
除了这些例子,当循环变量(无意地)在体内发生变化时,还有其他reasions使用小于或大于的运算符:
- Negations make code harder to understand
- 否定使代码更难理解
-
<
or>
is only one char, but!=
two - <或> 仅为一个char,但!= 2
#20
1
In addition to the various people who have mentioned that it mitigates risk, it also reduces the number of function overloads necessary to interact with various standard library components. As an example, if you want your type to be storable in a std::set
, or used as a key for std::map
, or used with some of the searching and sorting algorithms, the standard library usually uses std::less
to compare objects as most algorithms only need a strict weak ordering. Thus it becomes a good habit to use the <
comparisons instead of !=
comparisons (where it makes sense, of course).
除了不同的人提到它可以降低风险之外,它还减少了与各种标准库组件交互所需的函数过载的数量。例如,如果您希望您的类型可以存储在std::set中,或者用作std::map的键,或者与一些搜索和排序算法一起使用,标准库通常使用std:::less来比较对象,因为大多数算法只需要严格的弱排序。因此,使用< comparison而不是!= comparison(当然,这是有意义的)成为一个好习惯。
#21
0
There is no problem from a syntax perspective, but the logic behind that expression 5!=i
is not sound.
从语法的角度来看没有问题,但是这个表达式后面的逻辑是5!=我不健全。
In my opinion, using !=
to set the bounds of a for loop is not logically sound because a for loop either increments or decrements the iteration index, so setting the loop to iterate until the iteration index becomes out of bounds (!=
to something) is not a proper implementation.
在我看来,使用!=设置for循环的边界在逻辑上是不合理的,因为for循环要么增加要么减少迭代索引,所以将循环设置为迭代,直到迭代索引超出界限(!(对某事)不是一个恰当的执行。
It will work, but it is prone to misbehavior since the boundary data handling is lost when using !=
for an incremental problem (meaning that you know from the start if it increments or decrements), that's why instead of !=
the <>>==>
are used.
它可以工作,但是它很容易发生错误,因为在使用!=处理增量问题时,会丢失边界数据处理(这意味着您从一开始就知道它是递增还是递减),这就是为什么使用<>>= >的原因。
#1
292
while (time != 6:30pm) {
Work();
}
It is 6:31pm... Damn, now my next chance to go home is tomorrow! :)
6:31pm……该死,现在我回家的下一个机会是明天!:)
This to show that the stronger restriction mitigates risks and is probably more intuitive to understand.
这表明,更严格的限制可以降低风险,而且可能更容易理解。
#2
93
There is no technical reason. But there is mitigation of risk, maintainability and better understanding of code.
没有技术原因。但是降低了风险、可维护性和更好地理解代码。
<
or >
are stronger restrictions than !=
and fulfill the exact same purpose in most cases (I'd even say in all practical cases).
<或> 的限制比!=更严格,并且在大多数情况下(我甚至可以说在所有实际情况下)都实现了完全相同的目的。
There is duplicate question here; and one interesting answer.
这里有重复的问题;和一个有趣的答案。
#3
82
There are already answers, but there is another case, where the difference matters. If you have a loop like this:
已经有答案了,但还有另一种情况,差别很重要。如果你有这样一个循环:
for (int i = a; i < b; i++){}
this loop will work as expected for any values of a
and b
while if you used !=
you would have to check for a <= b
before running the loop.
这个循环对于a和b的任何值都将像预期的那样工作,如果您使用!=,那么您必须在运行循环之前检查a <= b。
So again it boils down to: In most cases there is no difference, but in some cases the small difference matters a lot. If you want to have all your loops look the same (and not for each loop have to consider which version is better) then just use <
and not !=
.
所以它可以归结为:在大多数情况下没有区别,但在某些情况下,小的差别是很重要的。如果你想拥有你所有的循环看起来一样(而不是为每个循环必须考虑哪个版本更好)然后用 <不! =。< p>
In some special cases you might need !=
and not <
, but these are really special cases and then it makes sense that they look different than the usual loops.
在某些特殊情况下,您可能需要!=而不是<,但这些是非常特殊的情况,因此它们看起来与通常的循环不一样。
When I first answered this question I considered only plain old loops with an int
counter. Once iterators enter the game, the situation is different. Loops with iterators usually look like this:
当我第一次回答这个问题时,我只考虑使用int计数器的普通旧循环。一旦迭代器进入游戏,情况就不同了。带有迭代器的循环通常是这样的:
for (Iterator it = v.begin(); it != v.end(); ++it);
The reason is* that it is a much weaker constraint to implement an !=
operator. Almost any two object can be tested for inequality, but not for all types a <
operator makes sense. Especially when the containers elements are not ordered the iterators cannot implement it. Thus the more generic way of writing such a loop is with !=
.
原因是,实现一个!=操作符的约束要弱得多。几乎任何两个对象都可以被测试为不平等,但对于所有类型的 <操作符都是有意义的。特别是当容器元素没有被排序时,迭代器无法实现它。因此,编写这样一个循环的通用方法是用!=。< p>
However, my point from above is still valid: There might be cases with an int
counter where you have to use !=
. They are rare and when they occur you want them to be easily identifiable. Thus it is common to write most int
-counter loops with <
.
但是,我上面的观点仍然是有效的:可能存在需要使用int计数器的情况!=。它们是罕见的,当它们出现时,你希望它们易于识别。因此,使用 <编写大多数的intcounter循环是很常见的。< p>
* = Comes without warranty. Please someone correct me if I am wrong ;)
没有担保就来了。如果我说错了,请指正。
#4
65
You can have something like
你可以有类似的东西
for(int i = 0; i<5; ++i){
...
if(...) i++;
...
}
If your loop variable is written by the inner code, the i!=5
might not break that loop. This is safer to check for inequality.
如果循环变量是由内部代码i编写的!=5可能不会破坏循环。检查不平等更安全。
Edit about readability. The inequality form is way more frequently used. Therefore, this is very fast to read as there is nothing special to understand (brain load is reduced because the task is common). So it's cool for the readers to make use of these habits.
编辑对可读性。不等式形式更常用。因此,这是非常快的阅读,因为没有什么特别的需要去理解(大脑负荷减少,因为任务是常见的)。所以读者利用这些习惯是很酷的。
#5
47
And last but not least, this is called defensive programming, meaning to always take the strongest case to avoid current and future errors influencing the program.
最后但并非最不重要的是,这被称为防御性编程,意思是始终采取最强的情况,以避免当前和未来的错误影响程序。
The only case where defensive programming is not needed is where states have been proven by pre- and post-conditions (but then, proving this is the most defensive of all programming).
唯一不需要防御性编程的情况是,状态已经被前置和后置条件所证明(然后,证明这是所有编程中最具防御性的)。
#6
37
I would argue that an expression like
我认为这样的表达方式
for ( int i = 0 ; i < 100 ; ++i )
{
...
}
is more expressive of intent than is
意图的表达比实际要多吗
for ( int i = 0 ; i != 100 ; ++i )
{
...
}
The former clearly calls out that the condition is a test for an exclusive upper bound on a range; the latter is a binary test of an exit condition. And if the body of the loop is non-trivial, it may not apparent that the index is only modified in the for
statement itself.
前者明确指出,条件是对范围上的唯一上限的测试;后者是出口条件的二进制测试。而且,如果循环的主体不是平凡的,那么索引可能不会明显地只在for语句本身中修改。
#7
20
Iterators are an important case when you most often use the !=
notation:
当您最经常使用!=符号时,迭代器是一个重要的例子:
for(auto it = vector.begin(); it != vector.end(); ++it) {
// do stuff
}
Granted: in practice I would write the same relying on a range-for
:
当然,在实践中,我也会根据范围写同样的东西——因为:
for(auto & item : vector) {
// do stuff
}
but the point remains: one normally compares iterators using ==
or !=
.
但重点仍然是:通常使用==或!=来比较迭代器。
#8
17
The loop condition is an enforced loop invariant.
循环条件是一个强制循环不变量。
Suppose you don't look at the body of the loop:
假设你不看循环的主体:
for (int i = 0; i != 5; ++i)
{
// ?
}
in this case, you know at the start of the loop iteration that i
does not equal 5
.
在这种情况下,您知道在循环迭代的开始,我不等于5。
for (int i = 0; i < 5; ++i)
{
// ?
}
in this case, you know at the start of the loop iteration that i
is less than 5
.
在这种情况下,你知道在循环迭代的开始i小于5。
The second is much, much more information than the first, no? Now, the programmer intent is (almost certainly) the same, but if you are looking for bugs, having confidence from reading a line of code is a good thing. And the second enforces that invariant, which means some bugs that would bite you in the first case just cannot happen (or don't cause memory corruption, say) in the second case.
第二个比第一个要多得多,不是吗?现在,程序员的意图(几乎是肯定的)是一样的,但是如果您正在寻找bug,那么阅读一行代码就有信心是一件好事。第二个是不变的,这意味着在第一个例子中会咬到你的一些错误在第二种情况下不会发生(或者不会导致内存损坏)。
You know more about the state of the program, from reading less code, with <
than with !=
. And on modern CPUs, they take the same amount of time as no difference.
您可以通过读取更少的代码,使用 <而不是!=来了解程序的状态。而在现代cpu上,它们所花费的时间是一样的。< p>
If your i
was not manipulated in the loop body, and it was always increased by 1, and it started less than 5
, there would be no difference. But in order to know if it was manipulated, you'd have to confirm each of these facts.
如果你的i在循环体中没有被操纵,它总是增加1,并且开始时小于5,那么没有区别。但是为了知道它是否被操纵,你必须确认每一个事实。
Some of these facts are relatively easy, but you can get wrong. Checking the entire body of the loop is, however, a pain.
有些事实相对容易,但你可能会出错。然而,检查整个回路是一种痛苦。
In C++ you can write an indexes
type such that:
在c++中,可以编写这样的索引类型:
for( const int i : indexes(0, 5) )
{
// ?
}
does the same thing as either of the two above for
loops, even down to the compiler optimizing it down to the same code. Here, however, you know that i
cannot be manipulated in the body of the loop, as it is declared const
, without the code corrupting memory.
做与上面两个循环中的任何一个一样的事情,甚至到编译器优化它到相同的代码。但是,在这里,您知道我不能在循环的主体中操作,因为它声明为const,而代码不会损坏内存。
The more information you can get out of a line of code without having to understand the context, the easier it is to track down what is going wrong. <
in the case of integer loops gives you more information about the state of the code at that line than !=
does.
在不需要理解上下文的情况下,从一行代码中获得的信息越多,就越容易跟踪出错的地方。 <在整型循环的情况下,与!=相比,提供了更多关于该行代码状态的信息。< p>
#9
12
As already said by Ian Newson, you can't reliably loop over a floating variable and exit with !=
. For instance,
正如Ian Newson所说,您不能可靠地循环一个浮动变量并使用!=退出。例如,
for (double x=0; x!=1; x+=0.1) {}
will actually loop forever, because 0.1 can't exactly be represented in floating point, hence the counter narrowly misses 1. With <
it terminates.
实际上会一直循环下去,因为0。1不能准确地用浮点数表示,所以计数器仅差1。 <它终止。< p>
(Note however that it's basically undefined behaviour whether you get 0.9999... as the last accepted number – which kind of violates the less-than assumption – or already exit at 1.0000000000000001.)
(但请注意,无论你是否得到0.9999,它基本上都是未定义的行为……)作为最后一个被接受的数字——这有点违反了小于假设——或者已经退出了1.0000000000000000000000000001)。
#10
12
It may happen that the variable i
is set to some large value and if you just use the !=
operator you will end up in an endless loop.
变量i可能被设置为某个大值,如果您只是使用!=操作符,那么您将会得到一个无穷无尽的循环。
#11
11
As you can see from the other numerous answers, there are reasons to use < instead of != which will help in edge cases, initial conditions, unintended loop counter modification, etc...
正如您可以从其他众多答案中看到的,使用 <而不是!=将有助于边的情况、初始条件、意外的循环计数器修改等等……< p>
Honestly though, I don't think you can stress the importance of convention enough. For this example it will be easy enough for other programmers to see what you are trying to do, but it will cause a double-take. One of the jobs while programming is making it as readable and familiar to everyone as possible, so inevitably when someone has to update/change your code, it doesn't take a lot of effort to figure out what you were doing in different code blocks. If I saw someone use !=
, I'd assume there was a reason they used it instead of <
and if it was a large loop I'd look through the whole thing trying to figure out what you did that made that necessary... and that's wasted time.
老实说,我认为你不能强调公约的重要性。对于这个示例,其他程序员很容易看到您正在尝试做什么,但这将导致重复。编程的工作之一就是让每个人都能尽可能地阅读和熟悉它,所以当有人必须更新/更改代码时,不可避免地,不需要花费很多精力去弄清楚您在不同的代码块中做了什么。如果我看到有人使用!=,我想他们使用它而不是 <是有原因的,如果是一个大的循环,我会仔细检查整个过程,试图弄清楚你做了什么,这是必要的……这是浪费时间。< p>
#12
9
There are several ways to write any kind of code (usually), there just happens to be two ways in this case (three if you count <= and >=).
有几种方法可以编写任何类型的代码(通常),在这种情况下只有两种方法(如果计数为<=和>=,则为三种)。
In this case, people prefer > and < to make sure that even if something unexpected happens in the loop (like a bug), it won't loop infinitely (BAD). Consider the following code, for example.
在这种情况下,人们更喜欢>和<,以确保即使在循环中发生了意外的事情(比如bug),它也不会无限循环(糟糕)。例如,考虑下面的代码。
for (int i = 1; i != 3; i++) {
//More Code
i = 5; //OOPS! MISTAKE!
//More Code
}
If we used (i < 3), we would be safe from an infinite loop because it placed a bigger restriction.
如果我们使用(i < 3),我们就可以避免无限循环,因为它有更大的限制。
Its really your choice whether you want a mistake in your program to shut the whole thing down or keep functioning with the bug there.
无论您是希望程序中出现错误,关闭整个程序,还是继续运行bug,这都是您的选择。
Hope this helped!
希望这帮助!
#13
9
I take the adjectival "technical" to mean language behavior/quirks and compiler side effects such as performance of generated code.
我用形容词“技术性”表示语言行为/怪癖和编译器副作用,如生成代码的性能。
To this end, the answer is: no(*). The (*) is "please consult your processor manual". If you are working with some edge-case RISC or FPGA system, you may need to check what instructions are generated and what they cost. But if you're using pretty much any conventional modern architecture, then there is no significant processor level difference in cost between lt
, eq
, ne
and gt
.
为此,答案是:没有(*)。(*)是“请参考您的处理器手册”。如果您正在使用一些edge-case RISC或FPGA系统,您可能需要检查生成的指令和它们的成本。但是,如果您使用的是几乎任何传统的现代架构,那么lt、eq、ne和gt之间在处理器级别上的成本上没有显著差异。
If you are using an edge case you could find that !=
requires three operations (cmp
, not
, beq
) vs two (cmp
, blt xtr myo
). Again, RTM in that case.
如果您使用的是一个边缘案例,您可以发现!=需要三个操作(cmp,不是,beq) vs . 2 (cmp, blt xtr myo)。在这种情况下,RTM。
For the most part, the reasons are defensive/hardening, especially when working with pointers or complex loops. Consider
在大多数情况下,原因是防御/强化,特别是在处理指针或复杂循环时。考虑
// highly contrived example
size_t count_chars(char c, const char* str, size_t len) {
size_t count = 0;
bool quoted = false;
const char* p = str;
while (p != str + len) {
if (*p == '"') {
quote = !quote;
++p;
}
if (*(p++) == c && !quoted)
++count;
}
return count;
}
A less contrived example would be where you are using return values to perform increments, accepting data from a user:
一个不那么做作的例子是,您使用返回值执行增量,接受来自用户的数据:
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i != len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
i += step; // here for emphasis, it could go in the for(;;)
}
}
Try this and input the values 1, 2, 10, 999.
试试这个,输入值1,2,10,999。
You could prevent this:
你可以防止这种情况:
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i != len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
if (step + i > len)
std::cout << "too much.\n";
else
i += step;
}
}
But what you probably wanted was
但你可能想要的是
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i < len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
i += step;
}
}
There is also something of a convention bias towards <
, because ordering in standard containers often relies on operator<
, for instance hashing in several STL containers determines equality by saying
对于 <也存在某种约定偏差,因为在标准容器中排序通常依赖于操作符<,例如,在几个stl容器中的散列通过声明确定相等性< p>
if (lhs < rhs) // T.operator <
lessthan
else if (rhs < lhs) // T.operator < again
greaterthan
else
equal
If lhs
and rhs
are a user defined class writing this code as
如果lhs和rhs是用户定义的类,则将此代码编写为
if (lhs < rhs) // requires T.operator<
lessthan
else if (lhs > rhs) // requires T.operator>
greaterthan
else
equal
The implementor has to provide two comparison functions. So <
has become the favored operator.
实现者必须提供两个比较函数。所以 <已成为受欢迎的操作符。< p>
#14
9
Yes; OpenMP doesn't parallelize loops with the !=
condition.
是的,OpenMP不会将循环与!=条件并行化。
#15
7
The most common reason to use <
is convention. More programmers think of loops like this as "while the index is in range" rather than "until the index reaches the end." There's value is sticking to convention when you can.
使用 <的最常见原因是约定。更多的程序员认为这样的循环是“当索引在范围内”,而不是“直到索引到达末尾”。只要有可能,坚持惯例是有价值的。< p>
On the other hand, many answers here are claiming that using the <
form helps avoid bugs. I'd argue that in many cases this just helps hide bugs. If the loop index is supposed to reach the end value, and, instead, it actually goes beyond it, then there's something happening you didn't expect which may cause a malfunction (or be a side effect of another bug). The <
will likely delay discovery of the bug. The !=
is more likely to lead to a stall, hang, or even a crash, which will help you spot the bug sooner. The sooner a bug is found, the cheaper it is to fix.
另一方面,这里的许多答案声称使用< form有助于避免错误。我认为在很多情况下,这只会帮助隐藏bug。如果循环索引应该达到最终值,而实际上它超出了这个值,那么会发生一些您没有预料到的事情,可能会导致故障(或者是另一个bug的副作用)。 <可能会延迟发现错误。更有可能导致失速、挂起,甚至崩溃,这将帮助您更快地发现bug。bug越早被发现,修复起来就越便宜。< p>
Note that this convention is peculiar to array and vector indexing. When traversing nearly any other type of data structure, you'd use an iterator (or pointer) and check directly for an end value. In those cases you have to be sure the iterator will reach and not overshoot the actual end value.
注意,这种约定是数组和向量索引特有的。当遍历几乎任何其他类型的数据结构时,您将使用迭代器(或指针)并直接检查最终值。在这些情况下,您必须确保迭代器将达到并不会超过实际的最终值。
For example, if you're stepping through a plain C string, it's generally more common to write:
例如,如果您正在处理一个普通的C字符串,那么通常写:
for (char *p = foo; *p != '\0'; ++p) {
// do something with *p
}
than
比
int length = strlen(foo);
for (int i = 0; i < length; ++i) {
// do something with foo[i]
}
For one thing, if the string is very long, the second form will be slower because the strlen
is another pass through the string.
首先,如果字符串很长,第二种形式会比较慢,因为strlen是另一种通过字符串的方式。
With a C++ std::string, you'd use a range-based for loop, a standard algorithm, or iterators, even if though the length is readily available. If you're using iterators, the convention is to use !=
rather than <
, as in:
使用c++ std::string,您将使用基于范围的for循环、标准算法或迭代器,即使长度很容易获得。如果您使用的是迭代器,则该约定是使用!=而不是<,如:
for (auto it = foo.begin(); it != foo.end(); ++it) { ... }
Similarly, iterating a tree or a list or a deque usually involves watching for a null pointer or other sentinel rather than checking if an index remains within a range.
类似地,迭代树、列表或deque通常需要查看空指针或其他标记,而不是检查索引是否仍然在范围内。
#16
7
One reason not to use this construct is floating point numbers. !=
is a very dangerous comparison to use with floats as it'll rarely evaluate to true even if the numbers look the same. <
or >
removes this risk.
不使用此结构的一个原因是浮点数。!=是一个非常危险的比较,使用浮动,因为它很少会评估为真,即使数字看起来是一样的。 <或> 消除了这种风险。
#17
7
There are two related reasons for following this practice that both have to do with the fact that a programming language is, after all, a language that will be read by humans (among others).
遵循这种实践有两个相关的原因,这两个原因都与编程语言毕竟是一种供人类阅读的语言这一事实有关。
(1) A bit of redundancy. In natural language we usually provide more information than is strictly necessary, much like an error correcting code. Here the extra information is that the loop variable i
(see how I used redundancy here? If you didn't know what 'loop variable' means, or if you forgot the name of the variable, after reading "loop variable i
" you have the full information) is less than 5 during the loop, not just different from 5. Redundancy enhances readability.
(1)有一点冗余。在自然语言中,我们通常提供的信息比完全必要的要多,就像错误校正代码一样。这里的额外信息是循环变量i(看到我如何使用冗余了吗?如果你不知道“循环变量”是什么意思,或者如果你忘记了变量的名字,在读取“循环变量i”之后,你会得到完整的信息)在循环期间小于5,而不仅仅是与5不同。冗余提高可读性。
(2) Convention. Languages have specific standard ways of expressing certain situations. If you don't follow the established way of saying something, you will still be understood, but the effort for the recipient of your message is greater because certain optimisations won't work. Example:
(2)约定。语言有特定的标准表达方式。如果你不遵循既定的表达方式,你仍然会被理解,但是对你的信息接收者的努力更大,因为某些优化不会起作用。例子:
Don't talk around the hot mash. Just illuminate the difficulty!
不要谈论热土豆泥。只是照亮了困难!
The first sentence is a literal translation of a German idiom. The second is a common English idiom with the main words replaced by synonyms. The result is comprehensible but takes a lot longer to understand than this:
第一句是德语习语的直译。第二种是常用的英语成语,主要用同义词代替。结果是可以理解的,但需要更长的时间来理解:
Don't beat around the bush. Just explain the problem!
别拐弯抹角了。只是解释这个问题!
This is true even in case the synonyms used in the first version happen to fit the situation better than the conventional words in the English idiom. Similar forces are in effect when programmers read code. This is also why 5 != i
and 5 > i
are weird ways of putting it unless you are working in an environment in which it is standard to swap the more normal i != 5
and i < 5
in this way. Such dialect communities do exist, probably because consistency makes it easier to remember to write 5 == i
instead of the natural but error prone i == 5
.
这是正确的,即使在第一个版本中使用的同义词恰好比英语习语中的常规词更适合这种情况。当程序员阅读代码时,同样的力量也会发挥作用。这也是为什么5 !这样的方言社区确实存在,可能是因为一致性使人们更容易记住写5 = i,而不是写自然但容易出错的i = 5。
#18
5
Using relational comparisons in such cases is more of a popular habit than anything else. It gained its popularity back in the times when such conceptual considerations as iterator categories and their comparability were not considered high priority.
在这种情况下,使用关系比较更像是一种流行的习惯。当迭代器类别和它们的可比性没有被认为是高优先级时,它在时代中得到了它的欢迎。
I'd say that one should prefer to use equality comparisons instead of relational comparisons whenever possible, since equality comparisons impose less requirements on the values being compared. Being EqualityComparable is a lesser requirement than being LessThanComparable.
我想说的是,人们应该尽可能地使用相等性比较而不是关系比较,因为相等性比较对所比较的值的要求较少。平等可比性比不可比性要求要低。
Another example that demonstrates the wider applicability of equality comparison in such contexts is the popular conundrum with implementing unsigned
iteration down to 0
. It can be done as
另一个例子证明了平等比较在这种上下文中更广泛的适用性,那就是将无符号迭代实现为0的普遍难题。可以这样做
for (unsigned i = 42; i != -1; --i)
...
Note that the above is equally applicable to both signed and unsigned iteration, while the relational version breaks down with unsigned types.
注意,上面的内容同样适用于有符号和无符号迭代,而关系版本则使用无符号类型进行分解。
#19
2
Besides the examples, where the loop variable will (unintentional) change inside the body, there are other reasions to use the smaller-than or greater-than operators:
除了这些例子,当循环变量(无意地)在体内发生变化时,还有其他reasions使用小于或大于的运算符:
- Negations make code harder to understand
- 否定使代码更难理解
-
<
or>
is only one char, but!=
two - <或> 仅为一个char,但!= 2
#20
1
In addition to the various people who have mentioned that it mitigates risk, it also reduces the number of function overloads necessary to interact with various standard library components. As an example, if you want your type to be storable in a std::set
, or used as a key for std::map
, or used with some of the searching and sorting algorithms, the standard library usually uses std::less
to compare objects as most algorithms only need a strict weak ordering. Thus it becomes a good habit to use the <
comparisons instead of !=
comparisons (where it makes sense, of course).
除了不同的人提到它可以降低风险之外,它还减少了与各种标准库组件交互所需的函数过载的数量。例如,如果您希望您的类型可以存储在std::set中,或者用作std::map的键,或者与一些搜索和排序算法一起使用,标准库通常使用std:::less来比较对象,因为大多数算法只需要严格的弱排序。因此,使用< comparison而不是!= comparison(当然,这是有意义的)成为一个好习惯。
#21
0
There is no problem from a syntax perspective, but the logic behind that expression 5!=i
is not sound.
从语法的角度来看没有问题,但是这个表达式后面的逻辑是5!=我不健全。
In my opinion, using !=
to set the bounds of a for loop is not logically sound because a for loop either increments or decrements the iteration index, so setting the loop to iterate until the iteration index becomes out of bounds (!=
to something) is not a proper implementation.
在我看来,使用!=设置for循环的边界在逻辑上是不合理的,因为for循环要么增加要么减少迭代索引,所以将循环设置为迭代,直到迭代索引超出界限(!(对某事)不是一个恰当的执行。
It will work, but it is prone to misbehavior since the boundary data handling is lost when using !=
for an incremental problem (meaning that you know from the start if it increments or decrements), that's why instead of !=
the <>>==>
are used.
它可以工作,但是它很容易发生错误,因为在使用!=处理增量问题时,会丢失边界数据处理(这意味着您从一开始就知道它是递增还是递减),这就是为什么使用<>>= >的原因。