Iterating over an array style question

I

Ian Shef

As for your code above, why not use:

BufferedReader rdr = ...;

String line = rdr.readLine();
while (line != null)
{
line = rdr.readLine();
}

That seems much clearer to me.
<snip>

In this case, clearer is not necessarily better. I see these issues:

-- In the original code, the variable line had exactly the scope that it
needed. In the "clearer" code, variable line has a greatly expanded scope
that can get you into trouble eventually. One example: Suppose that you
also have a String variable called mine. With the scope of line limited to
the for (...) statement, mistyping mine as line will lead to an error
caught at compile time. With the "clearer" code, the same mistyping will
lead to an execution bug.

-- The program text

line = rdr.readLine();

now occurs twice. This violates the principle known as Don't Repeat
Yourself. There are good reasons for following this principle.
 
T

Tom Anderson

I ran into an interesting piece of code today which I had a fairly
strong reaction to, and I was wondering if it was just me, or whether
other people feel the same way about it. I don't really want to bias
the sample by saying whether I liked the code or not, so I'll just ask,
how do you feel about the following code for iterating over an array?

int[] arr = {1, 2, 3, 4}

for(int i=arr.length; i-->0;)
{
System.out.println(arr);
}


That's genius, but i would immediately fire anyone who wrote it.

tom
 
T

Tom Anderson

What, you never heard about the famous `down to' operator in C? It's the
--> operator: i --> 0! :)

Of course. But for some reason, my compiler complains when i try to use
the up-from operator: i <-- 0. Must be buggy.

tom
 
A

Alex Mentis

Ian said:
<snip>

In this case, clearer is not necessarily better. I see these issues:

-- In the original code, the variable line had exactly the scope
that it needed. In the "clearer" code, variable line has a greatly
expanded scope that can get you into trouble eventually.

What you say about the expanded scope is true. It's a trade-off. If
that's an overriding concern, see Arne's code, which is at least better
than forcing a for loop to act like a while loop.
-- The program text

line = rdr.readLine();

now occurs twice. This violates the principle known as Don't Repeat
Yourself. There are good reasons for following this principle.

There is a difference between initializing a loop control variable
before a loop and modifying a loop control variable inside a loop.
This construct (while admittedly repetitive) is clear and is used in
languages that don't allow you to use assignments in Boolean tests.
The principle "Don't Repeat Yourself" is more applicable to repeating
code that is more appropriately generalized into a function.
 
T

Tom Anderson

Eric Sosman said:
One of its consequences is that `++' and `--' cannot be used in any
context where the value of the (sub-)expression is used: You could do
`--i;' but not `x = a[--i];', for example.

Well, yes. In fact, it's not so much a consequence as it is the central
intent.

Preach it. I decided the other day (while drinking) that i would start
thumping functional programming weenies who use the term 'side-effects'.
Statements in imperative languages don't have side-effects; they have
effects. That's what they're for.

tom
 
I

Ian Shef

What you say about the expanded scope is true. It's a trade-off. If
that's an overriding concern, see Arne's code, which is at least better
than forcing a for loop to act like a while loop.

Arne's code replaces the for loop with a while loop but has the same scope
issue. I see nothing wrong with using a for loop here. I guess that we
weigh the issues in the trade-off differently.
There is a difference between initializing a loop control variable
before a loop and modifying a loop control variable inside a loop.
This construct (while admittedly repetitive) is clear and is used in
languages that don't allow you to use assignments in Boolean tests.
The principle "Don't Repeat Yourself" is more applicable to repeating
code that is more appropriately generalized into a function.

Arne wrote "Two identical calls are not so nice."

I agree with Arne. There is a maintenance issue involved with remembering
to change both if you change one. While I admit to having done the same
thing a time or two, I am never comfortable in doing it.

Arne's code eliminates the duplicate call, but still has the scope issue.
I vote for the for-loop idiom.
 
D

Daniel Giaimo

     Chacun à son goût, but I can't generate much enthusiasm for
this rule.  One of its consequences is that `++' and `--' cannot be
used in any context where the value of the (sub-)expression is used:
You could do `--i;' but not `x = a[--i];', for example.

My own -- admittedly idiosyncratic -- preference is to forego use of
the increment and decrement operators precisely because they tend to
lead one to writing statements with side effects like
   x = a[--i];
For me at least this tends to buggy code when I need to modify this
line to
   x = weight*a[--i]; (sic)

So I just use
   i -= 1;
   x  = a;

and keep my life simple and my code clearer.

There's nothing unclear about the code you marked with "(sic) [sic]".

Unless you don't know Java.

JLS §15.7


There is nothing _undefined_ about it, but there is definitely something
unclear about it. Remember, you can't guarantee that the person that
wrote the code you are reading knows the language spec as well as you.
It is quite possible that they either never learned the Java order of
evaluation, or misremembered it and meant

i--;
x = weight*a;

and not

x = weight*a[i-1];
i--;

and that their code has a so-far undetected bug in it. With the way it
is written in the OP, you have to fully understand, not only what they
are trying to do, but also their background, before you know their
intention, and therefore what the code is meant to do.

Moreover you are depending more on the correctness of the adherence to
the spec of the particular Java compiler you are using in code that
depends on that kind of order of evaluation dependent logic. This
exposes you to potentially more problems if the implementation of the
spec is not perfect in certain areas.

Good programmers try to make their code as readable as possible. They
realize that it is very easy for others to forget, or misremember the
particular peculiarities of the language they are currently using when
moving between languages, such as which order a particular language
evaluates expressions in. Therefore, they try to avoid constructions
that depend on such things, especially when such constructions are not
nearly-universally idiomatic in that language.

Good programmers also depend on as few assumptions about the correctness
of the implementation of the libraries, hardware, compiler, etc. that
they are using. They realize that those things are not only likely, but
guaranteed, to have bugs somewhere, and that staying as close as possible
to the path that is most utilised by others is the easiest way to avoid
running into esoteric bugs that haven't been fixed yet.

I've probably given away my feelings about the original code I posted by
this small rant, so I'll make it explicit here. My opinion of that code
was overwhelmingly negative. It was made even more so when the engineer
that showed it to me told me that this was how he always iterated over an
array. I would never use that particular means of looping over an array
in code meant to potentially be read or maintained by someone else, nor
would I sign off on a code review for another engineer that did until he
changed it to a standard for loop idiom, or at the very least, made it a
while loop and not a for loop, but even that is dubious at best.

To me, and every style guide I've ever read for C, C++, Java, and every
other language that has a similar for loop syntax, the use of the for
loop implies a very specific contract to the reader. The first clause
tells you what the loop variables look like in the first iteration of the
loop. The second clause tells you when the loop will stop executing.
The final clause tells you how the loop variables get modified between
executions of the loop. Any violation of this contract should only be
done for a very good reason and should be documented with comments in the
code as to why that choice was made. The code as written in my original
post violates at least two of these contracts, and arguably a third by
its confusing use of a post-decrement operator in a boolean test, nor
does it make any attempt to explain to the reader why a non-standard
idiom was chosen for the loop.

--
Dan Giaimo
 
S

Stanimir Stamenkov

Wed, 24 Nov 2010 14:49:17 -0500, /Arne Vajhøj/:
Hm.

I checked the docs.

It seems as if you are correct.

But find instead of matches has the side effect. Eric probably got
those two mixed up.

Matcher.matches() has the side effect of changing the Matcher's state
pretty much like Matcher.find() - see
 
M

Martin Gregorie

Arne's code replaces the for loop with a while loop but has the same
scope issue. I see nothing wrong with using a for loop here. I guess
that we weigh the issues in the trade-off differently.
It seems to me that a small piece of syntactic sugar would be very
useful. The ability to write the following would clarify scoping a lot:

while (boolean b = true; b)
{
b = (--n > 0);
}

This does no more than bring while() into line with the for() construct.
If we had it, I bet it would see a lot of use.
 
E

Eric Sosman

As for your code above, why not use:

BufferedReader rdr = ...;

String line = rdr.readLine();
while (line != null)
{
line = rdr.readLine();
}

That seems much clearer to me.

It also (1) gives `line' a wider scope than the original,
and (2) writes the same piece of code twice, giving subsequent
programmers a chance to change one without changing the other
to match. (1) could be addressed by introducing another block:

{
String line = rdr.readLine();
while (line != null) {
...
line = rdr.readLine();
}
}

.... or by rearranging the duplication:

for (String line = rdr.readLine(); line != null;
line = rdr.readLine()) {
...
}

.... but I see no way to dismiss (2) -- the more serious objection --
without relying on side-effects. (Or on throwing an exception to
break out of a "normal" control flow, which is also Bad.)
Your second example doesn't appear to have a side-effect, so I'm not
sure what your point is there. match.matches() simply returns a
Boolean, which is what is expected in the condition part of the if
statement. It's not checking a condition and also changing some
variable before or after it's done.

Quoth the Javadoc, "More information about a successful match can
be obtained by querying the state of the matcher." The updating of that
"state" is a side-effect of match.matches(). If you don't think so,
pray explain why all of match.end(), match.group(2), and so on behave
differently before match.matches() than afterward.
 
E

Eric Sosman

Two identical calls are not so nice.


Hm.

I checked the docs.

It seems as if you are correct.

But find instead of matches has the side effect. Eric
probably got those two mixed up.

"If the match succeeds then more information can be obtained via
the start, end, and group methods," says the Javadoc. Before a
successful match (via any of the matching methods), all these throw
IllegalStateException; after a successful match they all deliver data.
Changing the internal state of the Matcher is a side-effect in my
book, just as much as changing the state of a BufferedReader by
calling readLine().
 
E

Eric Sosman

It seems to me that a small piece of syntactic sugar would be very
useful. The ability to write the following would clarify scoping a lot:

while (boolean b = true; b)
{
b = (--n> 0);
}

This does no more than bring while() into line with the for() construct.
If we had it, I bet it would see a lot of use.

We *do* have it, albeit with a different spelling:

for (boolean b = true; b; )
{
b = (--n > 0);
}

Change "while" to "for" and add one semicolon: net savings of one
keystroke, ignoring optional white space.
 
E

Eric Sosman

Chacun à son goût, but I can't generate much enthusiasm for
this rule. One of its consequences is that `++' and `--' cannot
be used in any context where the value of the (sub-)expression
is used: You could do `--i;' but not `x = a[--i];', for example.

My own -- admittedly idiosyncratic -- preference is to forego use of
the increment and decrement operators precisely because they tend to
lead one to writing statements with side effects like
x = a[--i];
For me at least this tends to buggy code when I need to modify this
line to
x = weight*a[--i]; (sic)

So I just use
i -= 1;
x = a;

and keep my life simple and my code clearer.


There's nothing unclear about the code you marked with "(sic) [sic]".

Unless you don't know Java.

JLS §15.7


Yes, we all know Java defines left-to-right operand evaluation.
But "defines" doesn't mean "clarifies," and I personally would not
write the line in question. (But then, that may be because I have a
long history with C, which allows the syntax but does *not* define
the outcome.)

YMMV, but keep your mileage far from me. ;-)
 
E

Eric Sosman

Arne said:
how
do you feel about the following code for iterating over an array?

int[] arr = {1, 2, 3, 4}

for(int i=arr.length; i-->0;)
{
System.out.println(arr);
}


I don't like it. Somewhere, I picked up a rule about how good code
shouldn't rely on the difference between the post-increment and
pre-increment operators.


Then there would not be a need for the two versions.

Arne


Technically there's not really a "need" for either. They're just
syntactic sugar. There are languages that get along fine without
increment and decrement operators at all.


There are also languages that get along without the syntactic
sugar of `for' and `while' and `do' and `switch'. The very first
programming language I used was one of those -- and I have no desire
to go back to it. (Or more precisely, to GOTO back to it.)
 
L

Lew

...

Chacun à son goût, but I can't generate much enthusiasm for
this rule. One of its consequences is that `++' and `--' cannot be
used in any context where the value of the (sub-)expression is used:
You could do `--i;' but not `x = a[--i];', for example.

My own -- admittedly idiosyncratic -- preference is to forego use of
the increment and decrement operators precisely because they tend to
lead one to writing statements with side effects like
x = a[--i];
For me at least this tends to buggy code when I need to modify this
line to
x = weight*a[--i]; (sic)

So I just use
i -= 1;
x = a;

and keep my life simple and my code clearer.

There's nothing unclear about the code you marked with "(sic) [sic]".

Unless you don't know Java.

JLS §15.7


There is nothing _undefined_ about it, but there is definitely something
unclear about it. Remember, you can't guarantee that the person that
wrote the code you are reading knows the language spec as well as you.
It is quite possible that they either never learned the Java order of
evaluation, or misremembered it and meant

i--;
x = weight*a;

and not

x = weight*a[i-1];
i--;

and that their code has a so-far undetected bug in it. With the way it
is written in the OP, you have to fully understand, not only what they
are trying to do, but also their background, before you know their
intention, and therefore what the code is meant to do.


Java programmers should be expected to know the language if they're being paid
to know the language. Not that they do, and the level of their mistakes so
far exceeds merely misremembering order of evaluation (left-to-right always,
as the referenced section mandates), and yet extends to such basics, that it
could cause despair to the less quixotic.
Moreover you are depending more on the correctness of the adherence to
the spec of the particular Java compiler you are using in code that

Wha...? What are you talking about? If it's a Java compiler it adheres to
the spec for the Java language.
depends on that kind of order of evaluation dependent logic. This
exposes you to potentially more problems if the implementation of the
spec is not perfect in certain areas.

Nonsense. The major compilers - notably Oracle's and IBM's - are always
available so that's never a problem. Furthermore, a compiler is not a "Java
compiler" unless it is perfect in all areas, otherwise Oracle sues the
manufacturer's ass.

Good programmers try to make their code as readable as possible. They

Which implies that the readers are also programmers, and therefore should be
expected to know the language.

Otherwise they should be asking, "Would you like fries with that?" instead.
realize that it is very easy for others to forget, or misremember the
particular peculiarities of the language they are currently using when

If you're one of those "others", you shouldn't be representing yourself as a
Java programmer yet.
moving between languages, such as which order a particular language
evaluates expressions in. Therefore, they try to avoid constructions
that depend on such things, especially when such constructions are not
nearly-universally idiomatic in that language.

That's nothing but an argument for shoddy ignorance and carelessness.
Good programmers also depend on as few assumptions about the correctness
of the implementation of the libraries, hardware, compiler, etc. that

No assumptions here. If it isn't a Java compiler, then the rules of Java do
not apply, but if it is, then they do. Your statement is not germane.
they are using. They realize that those things are not only likely, but
guaranteed, to have bugs somewhere, and that staying as close as possible
to the path that is most utilised by others is the easiest way to avoid
running into esoteric bugs that haven't been fixed yet.

So you justify ignorance of the language on the basis that, say, Oracle's Java
implementation and any other certified implementation doesn't actually adhere
to the spec to which it is certified to adhere?

Riiiight.
To me, and every style guide I've ever read for C, C++, Java, and every
other language that has a similar for loop syntax, the use of the for
loop implies a very specific contract to the reader. The first clause

Yes, that it adheres to the semantics of the 'for' loop, which includes all
the idioms others have posted for it in this thread
tells you what the loop variables look like in the first iteration of the
loop. The second clause tells you when the loop will stop executing.
The final clause tells you how the loop variables get modified between
executions of the loop. Any violation of this contract should only be
done for a very good reason and should be documented with comments in the

None of the examples shown violate this contract.
code as to why that choice was made. The code as written in my original
post violates at least two of these contracts, and arguably a third by
its confusing use of a post-decrement operator in a boolean test, nor

Actually it adheres to the contract mostly as you presented it. To reiterate
from that first post of yours:
for(int i=arr.length; i-->0;)
{
System.out.println(arr);
}


The first part tells you what the loop variables "look like", the second tells
you when the loop will stop executing, and there is no third part.

The decrement in the condition is what you'd put in a 'while' loop, rendering
a redundant reference to the loop variable, well, redundant.
does it make any attempt to explain to the reader why a non-standard
idiom was chosen for the loop.

I don't know why you call that idiom "non-standard". I've seen that idiom
dozens, perhaps hundreds of times since learning C, then C++, Java, etc. I
don't think that word means what you think it means.

Maybe the writer assumed the reader knew the programming language?
 
L

Lew

Lew said:
There's nothing unclear about the code you marked with "(sic) [sic]".

Unless you don't know Java.

JLS §15.7

Eric said:
Yes, we all know Java defines left-to-right operand evaluation.
But "defines" doesn't mean "clarifies," and I personally would not
write the line in question. (But then, that may be because I have a
long history with C, which allows the syntax but does *not* define
the outcome.)

YMMV, but keep your mileage far from me. ;-)

I'm not saying that the idiom is desirable, only that it is not unclear.

Assuming a reader who knows the very basics of the Java language, it's not
unclear.
 
S

Stefan Ram

Daniel Giaimo said:
how do you feel about the following code for iterating over an array?
int[] arr = {1, 2, 3, 4}
for(int i=arr.length; i-->0;)
{
System.out.println(arr);
}


The code seems to be syntactically correct, but lacks a
specification of its intended behavior, so, semantically,
I cannot make assertions about it.

I would have written the loop as

for( int i = arr.length - 1; i >= 0; --i )
java.lang.System.out.println( arr[ i ]);

, which is my »canonical« wording for a loop that counts
downwards in a numerical range.
I had a fairly strong reaction

You might ask the author about the code or suggest
improvements, but do this in a kind and calm way! The way
such issues are communicated among team members might be
more important for the project success than issues of
microstyle of source code.
 
S

Stefan Ram

Eric Sosman said:
Chacun à son goût, but I can't generate much enthusiasm for
this rule. One of its consequences is that `++' and `--' cannot
be used in any context where the value of the (sub-)expression
is used: You could do `--i;' but not `x = a[--i];', for example.

The usage in a »context where the value of the
(sub-)expression is used« is one of the the intended usages
of those operators in C. So those rules can be read as »Don't
write idiomatic code in C!« or »Don't use C!«.

Ok, that POV is possible, but inapproriate, when the
language to be used /is/ C.

I mention »C« above instead of Java, because C introduced
those operators and Java only copied them, but I would also
apply the above to Java. The authors of the JLS could have
restricted the use of ++ and -- to be the topmost operator
in a statement-expression, but they have chosen not to do this.
 
D

Daniel Giaimo

...

Chacun à son goût, but I can't generate much enthusiasm for
this rule. One of its consequences is that `++' and `--' cannot be
used in any context where the value of the (sub-)expression is used:
You could do `--i;' but not `x = a[--i];', for example.

My own -- admittedly idiosyncratic -- preference is to forego use of
the increment and decrement operators precisely because they tend to
lead one to writing statements with side effects like
x = a[--i];
For me at least this tends to buggy code when I need to modify this
line to
x = weight*a[--i]; (sic)

So I just use
i -= 1;
x = a;

and keep my life simple and my code clearer.


There's nothing unclear about the code you marked with "(sic) [sic]".

Unless you don't know Java.

JLS §15.7


There is nothing _undefined_ about it, but there is definitely
something unclear about it. Remember, you can't guarantee that the
person that wrote the code you are reading knows the language spec as
well as you. It is quite possible that they either never learned the
Java order of evaluation, or misremembered it and meant

i--;
x = weight*a;

and not

x = weight*a[i-1];
i--;

and that their code has a so-far undetected bug in it. With the way it
is written in the OP, you have to fully understand, not only what they
are trying to do, but also their background, before you know their
intention, and therefore what the code is meant to do.


Maybe the writer assumed the reader knew the programming language?

I'm not gonna bother responding to your post point by point since I don't
particularly enjoy arguing with either insane people or idiots, (which
you are I don't know, but you are quite clearly one of the two), but I
will say this: I'm glad you live in a perfect world where nothing fails
and all the code you have to read and maintain is perfect and does what
it is supposed to. Where you and all your coworkers know every detail of
every technology you use and never make a mistake or get confused. Where
nothing ever fails and all software and hardware is bug-free.

Sometimes I wish I lived in a perfect world too. Unfortunately, I live
in a very imperfect world, where neither I nor my coworkers know or
remember every detail of every piece of technology we use. Where
hardware fails to do what the specs say it does. Where software fails to
do what the specs say it does. Where bug reports are not instantly
turned around by the vendors of the products I use, indeed, are sometimes
never turned around at all. So I've developed certain techniques of
mitigating the damage I do and the damage other people do to me. Again,
I applaud the fact that you have no need of such things. It must be nice
to live in your world.

--
Dan Giaimo
 
S

Stefan Ram

Tom McGlynn said:
For me at least this tends to buggy code when I need to modify this
line to
x = weight*a[--i]; (sic)


Maybe we should refrain from using this argument:

»You should not use ... in code, because when you
later have to modify the code in that way, it
would be wrong (if one would forget to change
this part, too).«

For example, whats wrong in the following sentence?

»The girl takes the book.«

Well, look: There is nothing actually wrong, but one
should not use »takes«, because when one later has
to change »The girl« into »The girls«, it would be wrong:

»The girls takes the book.«

-- Such a way of reasoning surely is deemed to be ridiculous.

So why should one adopt it for source code?

The author of source code usually cannot foresee the
direction of possible future changes, so he should solve the
task at hand not a possible future task. When another
programmer later has the task to modify the code, it is
/his/ duty to do this correctly.

For¹ example,

for( int i = 0; i < n; ++i )java.lang.System.out.println( i );

. I assume that a possible future programmer, who also wants
to do something else in the loop will know when he has to
add braces or so.

¹) No pun intended.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,781
Messages
2,569,619
Members
45,308
Latest member
Foster Bronson

Latest Threads

Top