Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   Use != rather than < in for loops ? (http://www.velocityreviews.com/forums/t459737-use-rather-than-in-for-loops.html)

lovecreatesbea...@gmail.com 01-09-2007 09:30 AM

Use != rather than < in for loops ?
 
In C++ Primer 4th, sec 3.3.2, it states that C++ programmers use !=
rather than < in a for loop.

The following small snippet erases punctuations in a string. It works
well with < used in the for loop but it breaks when != is used instead.

#include <string>
#include <iostream>
#include <exception>
#include <cctype>
using namespace std;

int main(){
string s = "hello, world!";//s: i18n
//for (string::size_type sz = 0; sz < s.size(); ++sz){
/*works with < */
for (string::size_type sz = 0; sz != s.size(); ++sz){
/*doesn't work with != */
try{
if (ispunct(s.at(sz))){
s.erase(sz, 1);
}
}catch(exception &e){
cout << e.what() << endl;
}
}
cout << s << endl;
}


Rolf Magnus 01-09-2007 09:51 AM

Re: Use != rather than < in for loops ?
 
lovecreatesbea...@gmail.com wrote:

> In C++ Primer 4th, sec 3.3.2, it states that C++ programmers use !=
> rather than < in a for loop.


They do? Well, I do that when using iterators, but for a loop using an
integer loop counter, I use <.

> The following small snippet erases punctuations in a string. It works
> well with < used in the for loop but it breaks when != is used instead.
>
> #include <string>
> #include <iostream>
> #include <exception>
> #include <cctype>
> using namespace std;
>
> int main(){
> string s = "hello, world!";//s: i18n
> //for (string::size_type sz = 0; sz < s.size(); ++sz){
> /*works with < */
> for (string::size_type sz = 0; sz != s.size(); ++sz){
> /*doesn't work with != */
> try{
> if (ispunct(s.at(sz))){
> s.erase(sz, 1);
> }
> }catch(exception &e){
> cout << e.what() << endl;
> }
> }
> cout << s << endl;
> }


The problem is that s.size() will be reduced by 1 when you erase a
character. When the last character in your string is removed, your
index "jumps" over the end of the string, so sz will be greater than
s.size(). Therefore the version with < works, but not the one with !=.

Here is a table that shows what it looks like at the beginning of each loop
iteration:

index size character
0 13 h
1 13 e
2 13 l
3 13 l
4 13 o
5 13 , -> erased
6 12 w -> space is jumped over
7 12 o
8 12 r
9 12 l
10 12 d
11 12 ! -> erased
12 11 <none> -> index is past string end


Mark P 01-09-2007 09:51 AM

Re: Use != rather than < in for loops ?
 
lovecreatesbea...@gmail.com wrote:
> In C++ Primer 4th, sec 3.3.2, it states that C++ programmers use !=
> rather than < in a for loop.
>
> The following small snippet erases punctuations in a string. It works
> well with < used in the for loop but it breaks when != is used instead.
>
> #include <string>
> #include <iostream>
> #include <exception>
> #include <cctype>
> using namespace std;
>
> int main(){
> string s = "hello, world!";//s: i18n
> //for (string::size_type sz = 0; sz < s.size(); ++sz){
> /*works with < */
> for (string::size_type sz = 0; sz != s.size(); ++sz){
> /*doesn't work with != */
> try{
> if (ispunct(s.at(sz))){
> s.erase(sz, 1);
> }
> }catch(exception &e){
> cout << e.what() << endl;
> }
> }
> cout << s << endl;
> }
>


I don't have that book so I can only comment on what you've typed above.
The problem with the != version is that every time you call s.erase,
s.size() decreases by 1. Because s ends in a punctuation character you
erase the '!', thereby decrementing s.size() (12 to 11). Immediately
thereafter you increment sz (11 to 12), and the two values "pass
through" each other without ever being equal. Then you end up going out
of bounds with sz, throwing an out_of_range exception. In general I'd
prefer < to != for loop continuation conditions.

boaz1sade@gmail.com 01-09-2007 12:02 PM

Re: Use != rather than < in for loops ?
 

lovecreatesbea...@gmail.com wrote:
> In C++ Primer 4th, sec 3.3.2, it states that C++ programmers use !=
> rather than < in a for loop.
>
> The following small snippet erases punctuations in a string. It works
> well with < used in the for loop but it breaks when != is used instead.
>
> #include <string>
> #include <iostream>
> #include <exception>
> #include <cctype>
> using namespace std;
>
> int main(){
> string s = "hello, world!";//s: i18n
> //for (string::size_type sz = 0; sz < s.size(); ++sz){
> /*works with < */
> for (string::size_type sz = 0; sz != s.size(); ++sz){
> /*doesn't work with != */
> try{
> if (ispunct(s.at(sz))){
> s.erase(sz, 1);
> }
> }catch(exception &e){
> cout << e.what() << endl;
> }
> }
> cout << s << endl;
> }

Hi
The use of != is good practice when using iterators since the only type
of iterator that < will be meaningful to it is random access iterator
(the one used in vector and string but not in list, map ..). In this
case since you are not using iterators but comparing ints it is better
using < from the reasons the others pointed out. By the way I will
steer clean from this book. And anther by the way, its better to use
STL algorithms in this case and not to write this kind of explicit
loops so those type of troubles will not arise in the first place


lovecreatesbea...@gmail.com 01-09-2007 02:37 PM

Re: Use != rather than < in for loops ?
 

Rolf Magnus wrote:
> lovecreatesbea...@gmail.com wrote:
>
> > In C++ Primer 4th, sec 3.3.2, it states that C++ programmers use !=
> > rather than < in a for loop.

>
> They do? Well, I do that when using iterators, but for a loop using an
> integer loop counter, I use <.
>
> > The following small snippet erases punctuations in a string. It works
> > well with < used in the for loop but it breaks when != is used instead.
> >
> > #include <string>
> > #include <iostream>
> > #include <exception>
> > #include <cctype>
> > using namespace std;
> >
> > int main(){
> > string s = "hello, world!";//s: i18n
> > //for (string::size_type sz = 0; sz < s.size(); ++sz){
> > /*works with < */
> > for (string::size_type sz = 0; sz != s.size(); ++sz){
> > /*doesn't work with != */
> > try{
> > if (ispunct(s.at(sz))){
> > s.erase(sz, 1);
> > }
> > }catch(exception &e){
> > cout << e.what() << endl;
> > }
> > }
> > cout << s << endl;
> > }

>
> The problem is that s.size() will be reduced by 1 when you erase a
> character. When the last character in your string is removed, your
> index "jumps" over the end of the string, so sz will be greater than
> s.size(). Therefore the version with < works, but not the one with !=.
>
> Here is a table that shows what it looks like at the beginning of each loop
> iteration:
>
> index size character
> 0 13 h
> 1 13 e
> 2 13 l
> 3 13 l
> 4 13 o
> 5 13 , -> erased
> 6 12 w -> space is jumped over
> 7 12 o
> 8 12 r
> 9 12 l
> 10 12 d
> 11 12 ! -> erased
> 12 11 <none> -> index is past string end


Thank you, and thank you all.

Yes, the problem in my last code snippet is not because of those two
operators.
I write a new one. I hope this one doesn't have so many errors like the
last one.

#include <string>
#include <iostream>
#include <exception>
#include <cctype>
using namespace std;

int main(){
string s = "hello, world!";
string::size_type sz = s.size();

cout << s << endl;
for(string::size_type idx=0; idx!=sz; ++idx){
//...; idx<sz; ... works
try{
if(ispunct(s.at(idx))){
s.erase(idx, 1);
idx--;
sz--;
}
}catch(exception &e){
cout << e.what() << endl;
}
}
cout << s << endl;

return 0;
}


Andre Kostur 01-09-2007 02:49 PM

Re: Use != rather than < in for loops ?
 
Mark P <usenet@fall2005REMOVE.fastmailCAPS.fm> wrote in news:UAJoh.30298
$hI.526@newssvr11.news.prodigy.net:

> lovecreatesbea...@gmail.com wrote:
>> In C++ Primer 4th, sec 3.3.2, it states that C++ programmers use !=
>> rather than < in a for loop.


You may want to look at the context in which that's used. That "rule"
applies to using iterators in a loop.


[snip]

> of bounds with sz, throwing an out_of_range exception. In general I'd
> prefer < to != for loop continuation conditions.


I'd amend that statement to clarify about what your using as a loop
continuation condition. Using < when working with iterators may not work.


Daniel T. 01-09-2007 03:04 PM

Re: Use != rather than < in for loops ?
 
In article <1168335033.390867.303110@v33g2000cwv.googlegroups .com>,
"lovecreatesbea...@gmail.com" <lovecreatesbeauty@gmail.com> wrote:

> In C++ Primer 4th, sec 3.3.2, it states that C++ programmers use !=
> rather than < in a for loop.
>
> The following small snippet erases punctuations in a string. It works
> well with < used in the for loop but it breaks when != is used instead.
>
> #include <string>
> #include <iostream>
> #include <exception>
> #include <cctype>
> using namespace std;
>
> int main(){
> string s = "hello, world!";//s: i18n
> //for (string::size_type sz = 0; sz < s.size(); ++sz){
> /*works with < */
> for (string::size_type sz = 0; sz != s.size(); ++sz){
> /*doesn't work with != */
> try{
> if (ispunct(s.at(sz))){
> s.erase(sz, 1);
> }
> }catch(exception &e){
> cout << e.what() << endl;
> }
> }
> cout << s << endl;
> }


I think the above could be done in a better way:

int main() {
string s = "hello, world!";
// first skip through the string to the first punct
string::size_type pos = 0;
while ( pos != s.size() && !ispunct(s[pos]) )
++pos;
// if a punct was found...
if ( pos != s.size() )
{
// start shifting characters to the left,
// overwriting any puncts found along the way
string::size_type index = pos++;
while ( pos != s.size() ) {
if ( !ispunct(s[pos]) ) {
s[index++] = s[pos];
}
++pos;
}
s.resize( index );
}
cout << s << '\n';
}

Of course this simplest and most idiomatic way would be:

bool is_punct( char c ) {
return ispunct( c );
}

int main() {
string s = "hello, world!";
s.erase( remove_if( s.begin(), s.end(), &is_punct ), s.end() );
cout << s << '\n';
}

Dizzy 01-09-2007 04:27 PM

Re: Use != rather than < in for loops ?
 
Daniel T. wrote:

> In article <1168335033.390867.303110@v33g2000cwv.googlegroups .com>,
> "lovecreatesbea...@gmail.com" <lovecreatesbeauty@gmail.com> wrote:
> Of course this simplest and most idiomatic way would be:
>
> bool is_punct( char c ) {
> return ispunct( c );
> }
>
> int main() {
> string s = "hello, world!";
> s.erase( remove_if( s.begin(), s.end(), &is_punct ), s.end() );
> cout << s << '\n';
> }


I don't understand why do you need the is_punct() wrapper, cannot ispunct()
work directly as in:
s.erase( remove_if( s.begin(), s.end(), &ispunct ), s.end() );

--
Dizzy
http://dizzy.roedu.net


=?ISO-8859-15?Q?Juli=E1n?= Albo 01-09-2007 05:44 PM

Re: Use != rather than < in for loops ?
 
Dizzy wrote:

> I don't understand why do you need the is_punct() wrapper, cannot
> ispunct() work directly as in:
> s.erase( remove_if( s.begin(), s.end(), &ispunct ), s.end() );


There is a problem with the direct use of functions of the ispunct family,
his siganture and return type can give problems to choose the appropriate
template parameters. And there can be other problems if the implemenation
use signed char in the plain char type. The less problematic solution will
be a wrapper that calls ispunct (static_cast <unsigned char> (c) )

--
Salu2

Kaz Kylheku 01-10-2007 01:53 AM

Re: Use != rather than < in for loops ?
 
lovecreatesbea...@gmail.com wrote:
> In C++ Primer 4th, sec 3.3.2, it states that C++ programmers use !=
> rather than < in a for loop.


Not smart programmers, who understand what it means for a loop guard
test to check for the correct precondition.

Fact is, if there is some loop variable i, and a limiting value N, and
if it is incorrect to execute the loop body if i is equal to N, or if i
is greater than N, then the proper guard for executing that loop body
is (i < N).

The test (i != N) only works when other logic has already ensured that
(i <= N) is true. That other logic is usually the fact N is
nonnegative, and that i starts at value 0 (and so i <= N) is initially
true, and that i increments by one in each loop iteration toward the
limiting value, and that N does not change.

The correct test (i < N) doesn't rely on any such assumptions. It
doesn't matter what happens to the value of i in each iteration.

> The following small snippet erases punctuations in a string. It works
> well with < used in the for loop but it breaks when != is used instead.


That's because the body of the loop changes N. If N is a moving target,
then the invariant (i <= N) does not hold from one iteration to the
next. On one iteration, it could be that i == N - 1. But then, i
increments, and N decrements, so that i is suddenly N + 1.

> #include <string>
> #include <iostream>
> #include <exception>
> #include <cctype>
> using namespace std;
>
> int main(){
> string s = "hello, world!";//s: i18n
> //for (string::size_type sz = 0; sz < s.size(); ++sz){
> /*works with < */
> for (string::size_type sz = 0; sz != s.size(); ++sz){
> /*doesn't work with != */
> try{
> if (ispunct(s.at(sz))){
> s.erase(sz, 1);
> }


You have another bug here anyway, because if you erase an element of
the string, then you must not increment the sz index. By doing that,
you skip a character, which could be a punctuation character.

Imagine that the action of erase is like that of the Del key on your
keyboard in a typical text editor. When you use Del, the character
under the cursor is eaten, and everything after it moves one position
to the left. If you wanted to delete several punctuation characters in
a row, you'd type Del several times without having to move the cursor.

Similarly, the zs ``cursor'' must not move while characters are being
deleted; after each erasure, the next character to be processed shifts
into the current position, and so that position of the string must be
reevaluated.

If that logic is corrected, then the != test will work, since in any
given iteration, only the s.size() value or the sz value will change,
not both at the same time. Either the size decrements by one, bringing
it closer to sz, or sz increments by one closer to s.size().



All times are GMT. The time now is 10:47 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.