Recycling variables and performance

Started by
31 comments, last by mrbastard 12 years, 10 months ago
Inequality is used to reduce number of special cases when increment isn't being done by 1.

Human factors matter a lot more than technical details. The simplest way is to simply memoize that for loop looks like this:for (0; n)It means, we are working with n elements. This is then transcribed into general looping construct like this:while (i < n) {
// increment i
}
This form works for any increment.

For is just a specialization of general repetition, a syntactic sugar of sorts, and it translates identically:for (int i = 0; i < n; i++)

Extended approach of checking (i >= min) && (i < max) reduces human factor impact - regardless of how insides change or what is passed into construct, the code remains stable.

And if correctness matters, then either Ada or full invariant verification must be done anyway, so again, the style of for loop becomes irrelevant.


One can argue major or minor points here, but the Principle of least surprise always applies. Don't surprise me with exotic termination conditions or extravagant for constructs. It has all to do with common approach. Since != is uncommon, I'll need to first turn off the warning light that goes off in my head and then double check to see that author truly intended what is written.

It's simple - you might have studied with The Master, but the code I'll encounter will be written by a script kiddie who treats loop conditions as guidelines rather than rules.

It's the same thing, no?[/quote]Not quite, that is legacy fallback. Correct version would be:std::for_each();which does the proper thing - it eliminates for/while and instead applies operator to range. Iterators are also bound to range and possibly range checked. If used properly, they can never be invalid (std::advance and similar).

has contributed to the works of Stroustrup[/quote]
That guy invented C++. That's borderline insanity :P
Advertisement

Since the for loop counter do more than simple incrementation, you run the risk of creating an infinite loop with the == != syntax should your termination condition be incorrect, whereas a < or > will generally terminate the loop.
AFAIK, the "[font="Courier New"]i != end[/font]" style is more "C++ish" than the "[font="Courier New"]i < end[/font]" style.


The reason is that they mean completely different things -- the first means, iterate until the iterator equals the terminal value, the second means iterate as long as the iterator no longer appears before the terminal value in sequence.

For primitive iterators, they're much the same thing, but for complex iterators (i.e. the world of operator overloading) the second version is extremely more complex in cases where 'sequence' is costly to determine (e.g. a linked-list iterator).

The argument about stopping infinite loops in the case of a bad terminal value doesn't hold much water --- if you've got a bad terminal value, then your code is wrong. Making wrong code look like it's working is a very bad thing.

Actually I was taught so by a former colleague of mine who is a c++ guru and has contributed to the works of Stroustrup. I'll try to defend it as I practice it and as he originally argued well for its use. When you work with iterators for instance, you define range as a place to start and a place to end. I'm not doing anything differently by telling at exactly which value to stop at.


As Antheus already mentioned, using iterators in a for loop is legacy syntax. For loops predate iterators and standard library containers, and that syntax was retrofitted to emulate the foreach construct. Now that there is a proper foreach construct in the standard library, iterator syntax in a for loop for going over the elements of a container is no longer idiomatic.


Are you not aware of when to stop counting? If your code is broken, and you "accidentally" skip past the value to end at, you have much bigger issues than putting a const before a variable: you cannot defend how the code will behave.
[/quote]

Once again, we come back to floating point values. While using such values is less common than simple counters, there is no reason why I cannot loop over the interior of a circle in pi/variable increments and have my loop terminate once I have reached ~2pi. Or perhaps I simply want to loop from 0.0 to 1.0 by 0.1 increments, or perhaps I even want to have a variable store the way in which I want to increment that cannot be determined at compile time, but I know that I want to stop when I have exceeded such a value. Also, (i < 10) implies that the loop is valid only for numbers less than 10, (10 != i) implies that the loop is invalid for the value 10, and requires additional information to be parsed in order to fully understand the purpose and intent of the loop.

You can (somewhat rightfully) state that I should not be using a for loop, but I do not find that very compelling, primarily because of how Bjarne Stroustrup has described the for loop statement as "iterating over a sequence of numbers." A numeric sequence is an ordered list of numbers described by a discrete function, and optionally bounded. Referring to my previous post, the natural language description of a sequence whose bound is written (n < 10) is:

All positive integers less than 10.

Conversely, if the bound is (10 != n), the language is less clear:

All positive integers where 10 is not equal to n.

While anyone versed in boolean expressions can distinguish between the two, the natural language reading of one is more obvious than the other. Also, a sequence of numbers includes floating point values, or an unknown quantity (at compile time). When the sequence involves ascending values, the < operator always defines a perfect upper bound, and the the > operator a lower bound for descending values. (10 != i) simply describes a single invariant.


If I break something, I will instantly know because i write so that problems will show themselves at compile time.
[/quote]

I am unsure as to how (10 != i) will produce a compile time error as opposed to (i < 10).


You mention written works arguing against this practice, (error prone and against good coding style) -could you help me to the source for it? I cannot find it.
[/quote]

Firstly, in approximately 10 C++ specific books that I currently have sitting on my shelf (sorry, about 3 of those are various editions of the same book), none of them contain an example of a for loop using your suggested syntax. All of them use a variation of the for (int i = 0; i < 10; ++i) syntax. These books include:

Programming: Principles and Practice Using C++ - Bjarne Stroustrup
The C++ Programming Language - Bjarne Stroustrup
C++ Primer Plus - Stephen Prata (multiple editions)

I consider these three books and a similar book on the C++ Standard Library to be the definitive works on the C++ language.

In addition, I remember language covering a similar topic in Code Complete, but I do not currently have my copy on hand to quote the relevant passages.

I have multiple books on the following languages: C, Java, C#, python, etc. All of which share the C style for loop syntax, and all examples follow the exact same pattern. I like to believe that I have most of the essential and definitive treatises on the subjects they contain, and I have not seen one mention a example similar to the one you have brought up. I have worked with a few people who use it and have talked about it, but I they have always been in the minority. I suppose I could also do my own namedropping of several prominant founders of Linux and Bell Labs, but I think that is unnecessary.

As a parting word, I would also like to express how greatly I prefer functional language for syntax. The use of list and sequence syntax is much more descriptive, and I would say that the natural language parsing of functional syntax is closer to that of my suggested for syntax.


Happy for your input although this thread has somewhat strayed from the original purpose. ;-)
[/quote]

I certainly hope that you do not take this the wrong way, as I am a language nerd and will politely discuss any range of minutiae ad nauseum. No one is incorrect here.

AFAIK, the "[font="Courier New"]i != end[/font]" style is more "C++ish" than the "[font="Courier New"]i < end[/font]" style.


I have mostly seen this for iterators, save for a handful of other times, of which this thread comprises roughly 50% of that. Given the predilection of authoritative books to prefer the latter, I find it difficult to accept that the former is more "C++ish," but I am no true authority on the issue.


The reason is that they mean completely different things -- the first means, iterate until the iterator equals the terminal value, the second means iterate as long as the iterator no longer appears before the terminal value in sequence.
[/quote]

I slightly disagree with the wording, because it changes the boolean expression. If we are taking the boolean expression at face value, it means "iterate while the iterator is not equal to the terminal value" whereas the second is "iterate while the iterator is below the terminal value." I am adverse to using the term "iterator" in this instance, as it is more appropriate to use the term "loop counter" in the instance where we are using a number, rather than a true "iterator." In the case of a collection iterator, see below:


For primitive iterators, they're much the same thing, but for complex iterators (i.e. the world of operator overloading) the second version is extremely more complex in cases where 'sequence' is costly to determine (e.g. a linked-list iterator).
[/quote]

As Antheus and I have mentioned previously, when iterating over collections, you are actually referring to the foreach syntax, which now has its own construct in the standard library. Foreach and for are actually semantically different, and were only conflated because of the lack of language primitives in C and C++. Now that the primitives are adopted into the standard library, using a for statement in conjunction with a collection should only be used when dealing with a true "index" (or loop counter) rather than an iterator (in case of a simple array, or other collection allowing use of "[ ]" indexing) and only if you are not performing a true linear iteration of the collection (ie, iterating over every 3rd index).


The argument about stopping infinite loops in the case of a bad terminal value doesn't hold much water --- if you've got a bad terminal value, then your code is wrong. Making wrong code look like it's working is a very bad thing.
[/quote]

The mere termination of a loop should never be an indication of working code, as the loop should perform some function, and the results of that computation should be the metric by which the correctness of the loop is guaged. However, one syntax allows you to terminate when you cannot accurately predict the final absolute value by which the loop must terminate. For instance:

for (float i = 0.0f; 2 * PI != i; i += PI / 12.0)

Because of accumulating floating point inaccuracy, this could potentially fail. Also:

n = *some number based on user input that can be any positive whole number in the range 10 to 15 *
for (int i = 0; n != i; i += 3) // we want this to loop until i exceeds the user input value

Once again, when using the natural reading of a sequence function definition, we say we want to "start at x and take numbers less than the upper bound" rather than "start at x and take numbers until x is not equal to upper bound" as our sequence function does not always necessarily output the defined upper boundary, leaving the possibility to take numbers that exceed the upper bound. != defines a single invariant condition, < and > define an understood boundary.
Question, I know in the high level there is really no noticeable difference but technically speaking( in the digital logic sense ) a != b has less comparisons than a < b, right?
If a and b are say 2 bit variables then the logic to compare is

a != b => a0 ? b0 ? a1 ?,b1;

a < b => ( (b1 ? a1 ) ? a1) ? ( (b0 ? a0) ? a0)

I haven't done that for a while so I'm not sure if the above algebra is correct, specifically the '<' comparison.
Edge cases will show your design flaws in your code!
Visit my site
Visit my FaceBook
Visit my github

The reason is that they mean completely different things -- the first means, iterate until the iterator equals the terminal value, the second means iterate as long as the iterator no longer appears before the terminal value in sequence.
I slightly disagree with the wording, because it changes the boolean expression. If we are taking the boolean expression at face value, it means "iterate while the iterator is not equal to the terminal value" whereas the second is "iterate while the iterator is below the terminal value." I am adverse to using the term "iterator" in this instance, as it is more appropriate to use the term "loop counter" in the instance where we are using a number, rather than a true "iterator."[/quote]An iterator in C++ is just a concept. Integers don't actually fit the concept of an iterator because they can't be dereferenced, but IME it's still common to see integer-index loops written in the same style as iterator loops.

If you look at the code for for_each it will certainly be implemented via "while not equal to terminal" instead of "while below terminal", because the latter is simply incorrect with regards to this concept.

Float loops, or integers loops with a range of terminal values are of course different because they definitely don't fit into the iterator concept.

If you're just iterating through ranges of numbers, then it's a matter of style or logical requirement, but if you're writing algorithms that operate on the iterator concept, then it does become a black and white issue (sequence is not a requirement of the concept).

An iterator in C++ is just a concept. Integers don't actually fit the concept of an iterator because they can't be dereferenced, but IME it's still common to see integer-index loops written in the same style as iterator loops.


Once again, I must defer to the fact that I do not see this style used with numeric counted loops in any of the definitive reference material for the C++ language. I am open to the argument, but I find the < and > operators to be more idiomatic when describing boundary conditions. Think of this in regards to boolean logic in other statements such as if () constructs.


If you look at the code for for_each it will certainly be implemented via "while not equal to terminal" instead of "while below terminal", because the latter is simply incorrect with regards to this concept.

Float loops, or integers loops with a range of terminal values are of course different because they definitely don't fit into the iterator concept.
[/quote]

I understand this, which is why I specifically pointed out the difference. Iterators are different, and I do believe that I mentioned that multiple times in my previous posts. However, the example in the OP is of an integer counted loop, not an iterator example. Also, I specifically mentioned why iterating over a collection (foreach) is semantically different than the standard loop counted for. I am only disagreeing with your semantic interpretation in the case of a numeric loop counted for, which is the one presented by the OP in the OP.

As Antheus already mentioned, using iterators in a for loop is legacy syntax. For loops predate iterators and standard library containers, and that syntax was retrofitted to emulate the foreach construct. Now that there is a proper foreach construct in the standard library, iterator syntax in a for loop for going over the elements of a container is no longer idiomatic.

How can you say this with a straight face? A proper foreach? You call std::for_each a proper foreach?

This is a proper foreach:

foreach( oneElement in someVector)
{
....
}


C# has a proper foreach. Requiring one to provide a function object is not a proper foreach.

The closest you can get to a proper foreach is using C++0x's lamba syntax:

std::vector<int> someVec = {0, 1, 2, 3, 4};

std::foreach( someVec.begin(), someVec.end(), [](int oneElement)
{
...
});



Leaves a lot to be desired.

I'd like a real foreach in C++, but std::for_each certainly isn't one.


Using a built-in control structure to iterate over a container and using a higher-order construct that calls a function object on each member of a container are not the same thing.

It's absolutely disingenuous to claim that std::for_each is a replacement for the temporary solution of using a for loop. The syntax was not "retrofitted to emulate the foreach construct." Not only does that not make any sense, you can't provide any evidence for it. The iterator syntax is the way it is in order to emulate pointers from C, which already had iterator semantics.

Stroustrup himself prescribes a for loop as a "way of traversing a vector." See this powerpoint presentation, page 37.
http://www.stroustru..._containers.ppt

Back to (10 != i). We use != rather than < when iterating over a vector. Is this any less error prone than (10 != i)? No, it's not.

As Antheus and I have mentioned previously, when iterating over collections, you are actually referring to the foreach syntax, which now has its own construct in the standard library. Foreach and for are actually semantically different, and were only conflated because of the lack of language primitives in C and C++. Now that the primitives are adopted into the standard library, using a for statement in conjunction with a collection should only be used when dealing with a true "index" (or loop counter) rather than an iterator (in case of a simple array, or other collection allowing use of "[ ]" indexing) and only if you are not performing a true linear iteration of the collection (ie, iterating over every 3rd index).



Are you being serious here? You are implying that one should write a function object to represent the internals of what in the past would have been a for loop? We should just have hundreds of function objects all over our code? If you're seriously implying this then I'd hate to see your code.
I understand this, ..., and I do believe that I mentioned that multiple times in my previous posts.
You don't have to keep repeating yourself after every reply. I'm not attacking you or your choice of for loop syntax...

I was just sharing my experience and adding an explanation as to why "!=" syntax is popular -- the same reason why you see more "++i" in C++ for-loops compared to the usual "i++" from C for-loops . No need to get defensive.

This topic is closed to new replies.

Advertisement