| Author | Topic: loops |
| Paul Wheaton | posted
April 06, 1999 05:22 PM MT (US)
I just finished the first whack at describing how to use a loop. I would appreciate feedback. See www.eporkchop.com/college/loop.html |
| Frank Carver | posted
April 07, 1999 04:54 AM MT (US)
I bet you guessed I'd have an opinion, didn't you... In general it's OK, but I think you are doing "for" a "do while" loops a bit of a disservice. Detailed comments: 1. Be a little careful here. Java loops are not exactly the same as C/C++ loops. In java the expression between the parentheses of a while or in the middle of a for must be a strict boolean. In C/C++ it may be any expression which is automatically converted by applying the non-zero rule. 2. Where you say: while( )
{
}
I would prefer that you put some sort of "tokens" in the parentheses and the braces, like: while (boolean-expression)
{
some stuff
}
You do it for the "for" case.
If you think a concept like "boolean-expression" hasn't been explained yet, you can always refer out to another page. 3. I can see why you advocate using "done", but one of the readability tweaks that I try and apply to software I maintain is "test the positive case, not the negative". It helps avoid the easy-to-miss use of "!" and makes adding extra conditions using && and | | a lot less error prone. In your case I might say: boolean waitingForEsc = true ;
while ( waitingForEsc )
{
if ( userPressedEsc() )
{
waitingForEsc = false ;
}
else
{
System.out.println("not done yet");
}
}
4. You say:
The "while loop" can be used for all of your looping needs. Personally, I would stress the "can" rather than the "all", to imply that you never really need another type of loop, but they are available, and do have their place. 5. I think you should really justify why you dislike the other methods of exiting a loop ("break", "continue" and "return"). Just a blanket "don't" doesn't equip the student to evaluate their use in other peoples code, and can sometimes lead to over-complicated solutions. As with other things, I would try to find at least one case or rule of thumb for where they are useful, to show that they aren't in most cases. Similarly, I think you should mention or link to a page with a discussion of exceptions and how they can inadvertently exit loops before they have completed. 6. I think you are a little harsh on "for". I tend to use for loops more than while loops, as most cases I encounter have the same structure:- setup, check, and move on. For example, I find a case like: for (String s = in.readLine(); s != null; s = in.readLine())
{
do a lot of stuff involving s
}
easier to read and maintain,
as both "readLine" calls are in one place. There is a lot less chance of
missing one if it needs to be modified, or inadvertently excluding it with
an "if" in the body of the loop.
I consider the rule of thumb for use of a for loop is can you simply identify the three phases ("setup", "check", and "move on"), should the "move on" phase happen every time round the loop, is the "setup" only used in this one loop? Also you say: The "for loop" shorthand for this is:
int i ;
for( i = 0 ; i < 10 ; i++ )
{
System.out.println( i );
}
I would always say:
for(int i = 0 ; i < 10 ; i++ )
{
System.out.println( i );
}
to emphasize the fact that i
only makes sense in the context of the loop.
7. I know you don't like do..while, (I don't, really) but you ought to describe it in a little more detail. Just ranting about how it should never be used is not really helping the student. I feel it's better to give at least a rule of thumb for when it should be used, and by implication that it shouldn't be used in all other cases. I'm particularly worried by "When you have to try to explain to somebody what it is and why you use it". I may be tough, but I require that all people who work on projects with me know the language and tools they are using. If they are unfamiliar with something I get them trained up so they recognize it and know when and when not to use it The attitude "avoid parts of the language just because some readers might not know about them" has led to some of the worst code I have ever seen (admittedly, its opposite; "I'm just learning the language, so I'll use every single feature, however obscure or pointless" is just as bad). The problem is where do you stop. You regard "do..while" as too difficult, but I have encountered people who never use ++, --, += etc., using "a = a + 1" instead. I'm currently involved in a running "discussion" with a colleague who regards ?: as akin to black magic. 8. I applaud your introduction of some style issues, but it opens possibilities for a lot of other good hints. A few that I would like to see are: 8.1 Recommend explicitly the layout you use, (start and end blocks on the same column as the loop keyword; always have a block, even for only one statement) and justify it in terms of catching subtle "while(stuff);" bugs and making it easier to add extra code in the loop (especially temporary diagnostic print-outs). 8.2 Discuss what can be put in the "boolean" part of a while or for loop, and recommend that only simple expressions or boolean variables be used. Actively discourage the old C habit of putting assignments or other side-effects there. 8.3 Discuss the case of degenerate loops where there is no "body" (this is especially common with for loops used to scan things) and recommend a syntax to express these clearly (i.e.. avoid "for(...);" or "while(..);") 8.4. Discuss "always loops" e.g.. while(true), for( ; ; ) and recommend a syntax. 8.5. Discuss and discourage the use of loops as timers. This is a rare habit these days, but I still occasionally encounter the likes of: // wait for a while for (int i = 0; i < 1000; ++i) ;[This message has been edited by Frank Carver (edited April 07, 1999).] |
| Paul Wheaton | posted
April 07, 1999 10:08 AM MT (US)
Excellent post! If you don't mind, I would like to cut and paste what you said into a web page and make a link to it at the bottom of loop.html. In response to your list: 1) VERY good point. I will try to work that in somewhere. 2) Excellent suggestion. I will try to come up with something like that. 3) You make the ultimate point about testing the positive instead of the negative. But I've never come up with a generic positive that I like. Usually I want to stop the loop for more than one reason. So "waitingForEsc" won't work. I used to use "okay", "peachy" and "keepGoing", but I felt that they did not make my code as readable as "done". So I trade a negation for readability. A good trade in my book. 4) I like the way I do it. I'm trying to emphasize that it is possible to use the while loop for everything. Later I clarify that there are times that it is best to use a for loop. 5) I think that any experienced programmers will probably skip this page. So my audience is beginners. I don't want to overwhelm them. BUT! I think you have inadvertently provided the ultimate solution. By posting your message as a link along with these comments I'm making, this issue will be covered. Here is my response: I have never found a need for using "continue". When I examine what it does, it simply looks like poor programming practice. I think that anyone that is attempting to use "continue" should re-examine their code and find a way to complete their algorithm without it. Because, IMO, they are doing it wrong. Any use of "break" outside of a switch statement falls into the same category as "continue". Any use of "return" outside of being the last line in a method falls into the same category as "continue". 6a) I think that your example for loop is more complicated than the same thing as a while loop. Getting everything on one line doesn't make it clearer. Think of the first time you saw an "advanced" use of a for loop in C. I bet you had to re-read it six times before you got a good grip on what it was doing. I once wrote an essay on how the For loop is a breeding ground for bugs in C and C++. It was published in the November 95 issue of PC Techniques. I won't blather on about it now, but let me say that I think that if a blossoming developer favored the while loop over the for loop, we would all benefit in the long run. 6b) In my style sheet www.eporkchop.com/style.txt I mention that I prefer to keep the variable declaration outside of the for loop declaration so that it clarifies the scope. I think most people would look at that and assume that "i" would have a scope of inside the loop, when (I'm pretty sure) it is outside of the loop. Again, I sacrifice a line for what I think is clarity. 7) The do..while loop was designed to be used when your loop will always be done at least once. But this case can still always be handled by a regular while loop. When underexperienced programmers encounter a do..while, it becomes a breeding ground for bugs. More importantly, it is so rarely used, I think it is almost standard to just not use it. Training: I try to write my code so that it has the best possible chance of being clear to the next programmer to look at it. This next programmer may be thoroughly trained expert or may be someone who faked his way through the interview, or wasn't paying attention during the training. The do..while loop is definitely a soft spot for a lot of developers, so I never use it in my code. And I recommend to my students to do as I do: know what it is, but do not implement it yourself. 8) check out www.eporkchop.com/style.txt . I could really use some feedback on that too! 8.1) I think I have this covered in style.txt. Let me know if I left something out. 8.2) Excellent suggestion. I don't think I have that in my style sheet. I'll have to put that in! 8.3) Can you slap me with this one again? I'm not catching your drift. 8.4) I considered showing always and never loops to be educational, but felt that would be too much detail and would bore the reader. I know I have used always loops a time or two, but they were for stupid things. Is there a decent business case for an always loop? 8.5) A little too advanced for my reader. In C and C++ that was the best way to do timing on a PC that would be better than 18 clicks per second. But it would never work in Java. (there, now it has been mentioned!) |
| Frank Carver | posted
April 08, 1999 10:39 AM MT (US)
1, 2 thanks. 3. Hmm. This approach seems to contradict your "readable code" ethic with laziness. Perhaps the admonition should be "use a positive, specific meaningful name where possible; if it becomes clumsy, consider using a generic name, if that is clumsy, consider a negative"? 4. Fair enough. 5a. What worries me about making "target audience" assumptions, is that the audience don't know which assumptions you've made, or which shortcuts you've taken. This can reduce the credibility of the whole work. You've got to allow students to find their own path through the material, that's why the web is such a good medium for this sort of thing. I see a strong argument for using a lot more links in this material. Sorry, that was a bit fluffy. To rephrase: Where do you draw the line between a "beginner" and an "expert"? How does a student know where you've drawn the line? Anything in these pages should be correct, and where incomplete should indicate that it is, and link to further information. 5b. Break, return, continue.
Take for example the classic case of parameter verification. Imagine a method which must validate its input parameters and return an error if they are invalid, or process them if they are valid. Assume, for the moment that there are several arguments and the processing is a significant number of complex lines of code. I would tend to write something like:
String method(int a, int b, ...)
{
if (..validate a, b etc..)
{
return "Error";
}
... long processing ...
} The alternatives seem to be: 5b.1 Use an "else". This is superficially attractive, but in the setup I indicated that the processing is long and complex. The extra level of indentation can make the code harder to read, by forcing more linebreaks and so on. 5b.2 Create an extra non-validating method which the first one calls. Again, this can seem attractive, but involves the overhead of passing the several parameters in and the return value out, as well as adding an extra layer of confusion to the method names. Can you explain to me why my proposed solution is therefore "poor programming practice"? In each of the three cases (break, continue, return) it makes sense to me to consider them as "light-weight exceptions", which allow an exit from a local context under unusual conditions without disturbing the readability of the main code. Also, I find that the normal transformation to eliminate the use of these keywords involves (a) extra variables, often in fairly broad scope, and (b) extra levels of indentation and/or (c) more complex test clauses in while or for loops. I consider all of these to be indicators of poor practice - they certainly lead to bigger, woolier code. 6a. OK, getting everything on one line may not make it "clearer" in simple terms, but my key point is that maintainability is more than just code which reads like english. If you've ever tried to edit a complex text document you'll know that. My aim in suggesting the above example was to highlight one of the main causes of maintenance faults in my experience, that of not catching all references to something. I suggest that while my example may not look as pretty, it does have that benefit. I also feel that while I may have had to look at an "advanced use" several times before understanding it, afterwards I did understand it, so the time wasn't wasted. And such syntax does have the advantage of reminding a beginner or bluffer that they have to keep learning and concentrating. I've never heard of PC Techniques. Is your essay on the web? 6b. Got'cha. This was one of the things they changed between C++ and Java. In Java the iterator value is only in scope inside the loop. Try the following snippet:
public class T
{
public static void main(String[] args)
{
for (int i = 0; i < 10; ++i)
{
System.out.println(i);
}
System.out.println(i);
}
}
I'll comment on your style document
later ...
7a. Do while. OK, the burden of proof is on your side now. Can you give me any examples of where a do..while is more likely to be more buggy than a regular while? 7b. Training. I still hold that to dumb-down a project to suit imagined possible misunderstandings is misguided effort. You can't know in advance what anyone will find hard or easy. I'd much rather catch training issues early in a project than when the pressure is on to meet a slipping deadline. ... 8.3 I know you don't like 'for', but consider this example: // seek to end of stream "in" for (int i = in.read(); i != -1; i = in.read()) ;or the similar: while (in.read() != -1) ;The loop has a function, but no explicit body. I tend to drive home that, while such a construct is reasonable, putting the ';' at the end of the line is not. Syntactically it is not different, but it softens the critical eye from being able to spot a misplaced ';' after a for or while - which is a classic bug. I hope that all made sense. |
| Paul Wheaton | posted
April 08, 1999 11:05 AM MT (US)
5a) At some point I have to stop talking about loops and move on to talk about the bazillion other aspects of Java. My original intention was to direct all students to the Just Java 1.2 book. But my opinions grabbed me and made me write more! 5b) Please provide me with a chunk of code that demonstrates the value of one of these conditions - where doing it without the listed construct would be inappropriate. 6a) PC Techniques has been replaced by "Visual Developer". The last issue of PCT was in 97 (I think). Excellent magazine. I don't know if the PCT stuff is on-line anywhere. Doubt it. And I don't have an electronic version of it. I did find this tidbit in my C++ library docs: This loop construct replaces the most common use of "for" in C programming: A variable starts at zero, is incremented with each iteration and stops just before reaching the stop value. This macro is a simpler, more readable, less error prone loop construct. Replaceint I; for(I=0;I<15;I++) Beep(); with int I;
Note that in the "for"
version, "I" is referenced three times, there are
So what you are saying is that
public static void main(String[] args)
{
for (int i = 0; i < 10; ++i)
{
System.out.println(i);
}
System.out.println(i);
}
is the same as
public static void main(String[] args)
{
{
int i ;
for (i = 0; i < 10; ++i)
{
System.out.println(i);
}
}
System.out.println(i);
}
yes?
7a) The do..while buggy problem comes from lack of experience with the construct. Not from any flaw in the language. |