Blah, blah, blah.
Enough with the hand-waving. Here's an example of a non-recursive search.
https://dl.dropboxusercontent.com/u/316 ... kieCat.pas
See line 11839, the start of SpookyFindMove. The node processing switch statement (actually a case statement in Pascal) starts at line 12042.
Where is enormous pile of knots?
Where is the extremely complex control flow?
Where is the mess of control structures?
goto thread (split)
Moderators: hgm, Rebel, chrisw
-
- Posts: 5106
- Joined: Tue Apr 29, 2008 4:27 pm
Re: Some statistics about promotions and underpromotions.
In particular the last article you referenced seems to be well thought out and balanced:Gerd Isenberg wrote:Too dogmatic for my taste - Dijkstra versus Knuth and Torvalds. I find it ugly if you introduce boolean variables only to avoid gotos, breaks or continues.sje wrote:A goto is ugly. A setjmp() is uglier. Even continue is bad. There are absolute standards which do not depend on the skill of a coder.
some links:
http://en.wikipedia.org/wiki/Goto
http://en.wikipedia.org/wiki/Considered_harmful
http://www.codinghorror.com/blog/2007/1 ... l-too.html
http://stevemcconnell.com/ccgoto.htm
http://stevemcconnell.com/ccgoto.htm
Capital punishment would be more effective as a preventive measure if it were administered prior to the crime.
-
- Posts: 2684
- Joined: Sat Jun 14, 2008 9:17 pm
Re: Some statistics about promotions and underpromotions.
The amazing thing is that after 45 (!) years:
http://www.u.arizona.edu/~rubinson/copy ... rmful.html
People is _still_ discussing about this. It reminds me of the discussions of old people at the pubs that still dispute if the trainer took the right decision to change that soccer forwarded for the other one on a match of the '64 or something...and I, saw with my eyes, they can even get very nervous while discussing about this
http://www.u.arizona.edu/~rubinson/copy ... rmful.html
People is _still_ discussing about this. It reminds me of the discussions of old people at the pubs that still dispute if the trainer took the right decision to change that soccer forwarded for the other one on a match of the '64 or something...and I, saw with my eyes, they can even get very nervous while discussing about this
-
- Posts: 931
- Joined: Tue Mar 09, 2010 3:46 pm
- Location: New York
- Full name: Álvaro Begué (RuyDos)
Re: Some statistics about promotions and underpromotions.
I don't follow any of the rules that Steven Edwards advocates. I try to write code in the way that it best expresses my intent, and that often involves using continue, break, multiple returns and rarely (but not never) even goto.
If you are writing C and this is the type of function that is likely to have the need for destructor-type code, sure, it's best to avoid early returns. But then you'll probably end up using a break statement. If you don't do that either, you need to create a boolean variable to decide if you should do another iteration of the loop or not, and that's an ugly hack in my opinion.
Here's some code to illustrate what I am talking about:
Even this one I don't agree with. If I get to the point in my function where I know what I need to return, an early return statement is the cleanest way to express that.Don wrote:I don't believe in absolutes but it's a very good rule of thumb to avoid multiple returns. One exit point only is almost always a good policy for code readability.
I have been programming in C++ for a really long time now, and I find that the overwhelming majority of the situations where you need to do something just before returning from a function is best represented by invoking the destructor of some object. In that case, the early return works just fine.Sometimes it will happen that you need to do something just before returning from a function and if you have multiple exit points you must code this everywhere you do a return, a lot of code duplication and that is a generally a sign you are doing something wrong.
If you are writing C and this is the type of function that is likely to have the need for destructor-type code, sure, it's best to avoid early returns. But then you'll probably end up using a break statement. If you don't do that either, you need to create a boolean variable to decide if you should do another iteration of the loop or not, and that's an ugly hack in my opinion.
Here's some code to illustrate what I am talking about:
Code: Select all
bool is_prime(unsigned n) {
if (n%2 == 0)
return n==2;
for (int k = 3; k*k <= n; k += 2) {
if (n%k == 0)
return false;
}
return true;
}
-
- Posts: 2559
- Joined: Fri Nov 26, 2010 2:00 pm
- Location: Czech Republic
- Full name: Martin Sedlak
Re: Some statistics about promotions and underpromotions.
Good, yet another flamewar
I personally see nothing wrong about a goto (especially when it saves extra code), I'm not using it that often however (almost never in fact).
I find it insane to not use continue/break.
I prefer
over
which creates extra level of nesting, making the code less readable.
To add more oil: I also find exceptions aka supergoto over multiple stack frames absolutely useless, they just suck
They add unwinding tables/code (=size overhead) to resulting binary (note that this is only a problem in C++, not in GC-based languages).
Plus wonderful constructs like
instead of
And of course using throw exception instead of return code.
Needless to say it's wonderful that you debug some code and suddenly find yourself somewhere else inside a catch block.
Some people even misuse exceptions to pass return values, which is a very bad practice.
OT: Multiple inheritance also sucks as it's often used to glue completely unrelated classes together (it also makes things a lot more complicated, potentially requiring virtual inheritance).
I personally see nothing wrong about a goto (especially when it saves extra code), I'm not using it that often however (almost never in fact).
I find it insane to not use continue/break.
I prefer
Code: Select all
for (...)
{
if ( condition_not_met )
continue;
do stuff
}
Code: Select all
for (...)
{
if ( condition_met )
{
do stuff
}
}
To add more oil: I also find exceptions aka supergoto over multiple stack frames absolutely useless, they just suck
They add unwinding tables/code (=size overhead) to resulting binary (note that this is only a problem in C++, not in GC-based languages).
Plus wonderful constructs like
Code: Select all
try
{
do stuff
}
catch( type1 )
{
}
catch( type2 )
{
}
catch( ... )
{
}
Code: Select all
errcode = do stuff
switch( errcode )
{
case a:
....
case b:
....
default:
....
}
Needless to say it's wonderful that you debug some code and suddenly find yourself somewhere else inside a catch block.
Some people even misuse exceptions to pass return values, which is a very bad practice.
OT: Multiple inheritance also sucks as it's often used to glue completely unrelated classes together (it also makes things a lot more complicated, potentially requiring virtual inheritance).
-
- Posts: 5106
- Joined: Tue Apr 29, 2008 4:27 pm
Re: Some statistics about promotions and underpromotions.
Note that I am speaking from a C background so your next statement doesn't apply. 9 times out of 10 this comes under the same category as good use of continue and break but my own experience has followed this cycle too often:AlvaroBegue wrote:I don't follow any of the rules that Steven Edwards advocates. I try to write code in the way that it best expresses my intent, and that often involves using continue, break, multiple returns and rarely (but not never) even goto.
Even this one I don't agree with. If I get to the point in my function where I know what I need to return, an early return statement is the cleanest way to express that.Don wrote:I don't believe in absolutes but it's a very good rule of thumb to avoid multiple returns. One exit point only is almost always a good policy for code readability.
1. several returns are coded because it is easier to code.
2. discovery that I need some sort of common "finish-up" code.
3. a single bug as a result that has to be fixed in several places.
4. OR a syntax bug in just one of those places that has to be tracked down.
A common use example of this is when debugging. I will sometime insert a board display call with some information on exit that has to do with the current state at exit time. I have to go in and make that change in several places. Then I take it out which increase the chance of doing this with additional bugs. If this usage example causes a problem then to me it means I'm taking a chance when doing this. Having said that, I do not follow my own advice but will have multiple return statements especially when I feel there is little chance of this happening.
It's a real problem because when reading the same code weeks or months later, the multiple returns quite often turn into "gotcha's", it is really nice to know that there is only one way out of a routine. I find myself using my editor a lot to search for "return" in the code instead of just going to the end of the routine - so that tells me a LOT.
Nevertheless, each person has his own coding style and there may be other things I do that exacerbate this problem that you don't do. That is why there is wisdom in not making rules, let each person make their own rules but not impose them on others.
I have been programming in C++ for a really long time now, and I find that the overwhelming majority of the situations where you need to do something just before returning from a function is best represented by invoking the destructor of some object. In that case, the early return works just fine.Sometimes it will happen that you need to do something just before returning from a function and if you have multiple exit points you must code this everywhere you do a return, a lot of code duplication and that is a generally a sign you are doing something wrong.
If you are writing C and this is the type of function that is likely to have the need for destructor-type code, sure, it's best to avoid early returns. But then you'll probably end up using a break statement. If you don't do that either, you need to create a boolean variable to decide if you should do another iteration of the loop or not, and that's an ugly hack in my opinion.
Here's some code to illustrate what I am talking about:
Code: Select all
bool is_prime(unsigned n) { if (n%2 == 0) return n==2; for (int k = 3; k*k <= n; k += 2) { if (n%k == 0) return false; } return true; }
Another word about continue and break. I really hate my code to have very many nested compound statements. It gets very confusing after a while. I find it MUCH more readable to test and continue or test and break under some situations if the alternative is yet another level of indentation and nest when there are already many or if the alternative is to have a 200+ character set of tests combined with '&&' and '||' some of which are protected by parenthesis and make you go crossed-eyed trying to figure out what it means. For those who abhor continue, please don't try to tell me that is more readable or likley to be less buggy.
Capital punishment would be more effective as a preventive measure if it were administered prior to the crime.
-
- Posts: 3232
- Joined: Mon May 31, 2010 1:29 pm
- Full name: lucasart
Re: Some statistics about promotions and underpromotions.
Yes, that's a very good article on the subjects.Don wrote:In particular the last article you referenced seems to be well thought out and balanced:Gerd Isenberg wrote:Too dogmatic for my taste - Dijkstra versus Knuth and Torvalds. I find it ugly if you introduce boolean variables only to avoid gotos, breaks or continues.sje wrote:A goto is ugly. A setjmp() is uglier. Even continue is bad. There are absolute standards which do not depend on the skill of a coder.
some links:
http://en.wikipedia.org/wiki/Goto
http://en.wikipedia.org/wiki/Considered_harmful
http://www.codinghorror.com/blog/2007/1 ... l-too.html
http://stevemcconnell.com/ccgoto.htm
http://stevemcconnell.com/ccgoto.htm
I think that logical complexity, and deep nesting level is very important, and a problem often minimized or overlooked by anti-goto djihadists.
I use a goto in an unusual place in my code. It doesn't fall into the categories of accepted goto use, so it's an heresy in some sense. But when it's right, you just know it, regardless of the "rules"...
I have a function that plays a legal move on the board. It's already complex and deeply nested, and then I add null move in the search so I need to handle those. I could do a different function to play a null move, but that's not an acceptable solution because they would share a bunch of code (change turn of play and xor it in the zobrist among many other things). This shared code is especially non trivial and it's easy to screw it, so duplicating it is a recipie for disaster. So it all has to be in the same function.
But handling the null move would mean add extra nesting and make the function less readable like so:
Code: Select all
if (move != null_move) {
// play a legal move
}
// finish section
Instead I opted for
Code: Select all
if (move == null_move) goto finish;
// play a legal move
finish:
Some people may disagree with that though. Beauty is in the eye of the beholder as you said.
But in reality, the enemy is neither goto, setjmp, longjmp, break, continue, etc. The enemy is complexity which correlates with bugs and with hard to maintain code. An abuse of goto leads to complexity, but so does an abuse of control structures (example of deeply nested ifs which are *much worse* than a goto).
In the purest sense, if's are bad, loops are bad, variables (i/o constants) are bad. If you look at code written in functional languages like CAML or Haskell, it gives you an idea of how to remove possiblity of bugs and make code *provably correct* by using recursions everywhere. It's not efficient like C, but in cases where the algorithms are highly complex and bugs cannot be tolerated, it may be the right choice.
The various keywoprds of the language are just what they are. They are neither bad nor good, it all depends how we use them.
So the only dogma we can all agree to isthat bad code is bad (and bad code can be written with or without goto...)
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
-
- Posts: 5566
- Joined: Tue Feb 28, 2012 11:56 pm
Re: Some statistics about promotions and underpromotions.
I think the problem is that people are interpreting Dijkstra's paper outside its proper context. He was just making the point that "unbridled use of the go to statement" leads to hard to understand code at a time when people had not grown up with the idea of structured programming. Nowadays this is telling nothing new at all, so instead some people turn to a dogmatic religious view of Dijkstra's paper.mcostalba wrote:The amazing thing is that after 45 (!) years:
http://www.u.arizona.edu/~rubinson/copy ... rmful.html
People is _still_ discussing about this. It reminds me of the discussions of old people at the pubs that still dispute if the trainer took the right decision to change that soccer forwarded for the other one on a match of the '64 or something...and I, saw with my eyes, they can even get very nervous while discussing about this
Steven is going even further than that: he wants no go to statement, no recursion, no break, no continue, and a single exit point. Five things that mutually bite each other. The result can only be an explosion of nesting level and boolean variables.
I just found Knuth's paper on go to.
So maybe next time someone indicates that Fortran is a useful tool for him that gets his job done, let's not immediately take over the thread by praying to god to save his soul.Knuth wrote:I believe that by presenting such a view I am not in fact disagreeing sharply with Dijkstra's ideas, since he recently wrote the following: "Please don't fall into the trap of believing that I am terribly dogmatical about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!". In other words, it seems that fanatical advocates of the New Programming are going overboard in their strict enforcement of morality and purity in programs.
-
- Posts: 5106
- Joined: Tue Apr 29, 2008 4:27 pm
Re: Some statistics about promotions and underpromotions.
Probably most of us enjoy writing software to one extent or another but some people are more into the "processes" or the craft of doing it than others. For me, even though I do enjoy it, I see it as an obstacle in standing between me and the thing I want to build. I think for Steven it is the building itself that is the fun part. I'm definitely not above an ugly hack to get the job done and that has bit me many times. I don't believe goto, break and continue is an ugly hack but perhaps to someone like Steven it is. It's his "rock and roll" and to some people if it isn't rock and roll it isn't music.syzygy wrote:I think the problem is that people are interpreting Dijkstra's paper outside its proper context. He was just making the point that "unbridled use of the go to statement" leads to hard to understand code at a time when people had not grown up with the idea of structured programming. Nowadays this is telling nothing new at all, so instead some people turn to a dogmatic religious view of Dijkstra's paper.mcostalba wrote:The amazing thing is that after 45 (!) years:
http://www.u.arizona.edu/~rubinson/copy ... rmful.html
People is _still_ discussing about this. It reminds me of the discussions of old people at the pubs that still dispute if the trainer took the right decision to change that soccer forwarded for the other one on a match of the '64 or something...and I, saw with my eyes, they can even get very nervous while discussing about this
Steven is going even further than that: he wants no go to statement, no recursion, no break, no continue, and a single exit point. Five things that mutually bite each other. The result can only be an explosion of nesting level and boolean variables.
I just found Knuth's paper on go to.So maybe next time someone indicates that Fortran is a useful tool for him that gets his job done, let's not immediately take over the thread by praying to god to save his soul.Knuth wrote:I believe that by presenting such a view I am not in fact disagreeing sharply with Dijkstra's ideas, since he recently wrote the following: "Please don't fall into the trap of believing that I am terribly dogmatical about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!". In other words, it seems that fanatical advocates of the New Programming are going overboard in their strict enforcement of morality and purity in programs.
I have noticed over the years that Steven loves structure and process - and you can see that is evident in the awesome work he has done with the chess standards of PGN, FEN, EPD, that we we all use. We owe him a debt of gratitude for these things. I don't think I have ever taken the time to tell him this, so hopefully he is reading it now. Thank you Steven!!
Capital punishment would be more effective as a preventive measure if it were administered prior to the crime.
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Re: Bull
I am not saying that recursion is bad. Symbolic uses recursion as part of its perft(). But perft() is much different from a regular search as I don't expect a perft() call to be interrupted by a time-out or user intervention. A regular search must be able to stop gracefully with its partial results intact. Throwing an exception or calling longjmp() should be used for a truly exceptional condition, like power failure or processor-on-fire.
A new problem occurs with the introduction of a multithreaded search where the search continues while individual threads must be repeatedly re-run with different data. In addition to the requirement to stop gracefully, there is now the need to share data among the threads. It's tough to do this when a lot of interesting data is hidden on the inaccessible activation frames on different stacks local to each thread. With an old style recursive approach, it can be very hard to pause a thread, save its state, and later resume that thread.
Now, with an non-recursive approach, all of the above problems may be solved without excessive anguish. Also, with the details of a thread's state explicitly available and not hidden, a thread could even be restarted on a different machine with a different architecture as long as the semantics remained constant. Indeed, this is one of my goals with Symbolic; to allow multiple machines on the LAN to contribute to a search in a very integrated, yet portable, manner.
General comment:
I have noted over many years of professional experience that if a shop allows coders to use unstructured techniques, then the result is nearly always crappy code. It is also expensive code, for it much more often needs costly maintenance and modification. Yet this is tolerated because that extra expense may be pushed to the next business quarter. Sadly, this has become the American Way.
A new problem occurs with the introduction of a multithreaded search where the search continues while individual threads must be repeatedly re-run with different data. In addition to the requirement to stop gracefully, there is now the need to share data among the threads. It's tough to do this when a lot of interesting data is hidden on the inaccessible activation frames on different stacks local to each thread. With an old style recursive approach, it can be very hard to pause a thread, save its state, and later resume that thread.
Now, with an non-recursive approach, all of the above problems may be solved without excessive anguish. Also, with the details of a thread's state explicitly available and not hidden, a thread could even be restarted on a different machine with a different architecture as long as the semantics remained constant. Indeed, this is one of my goals with Symbolic; to allow multiple machines on the LAN to contribute to a search in a very integrated, yet portable, manner.
General comment:
I have noted over many years of professional experience that if a shop allows coders to use unstructured techniques, then the result is nearly always crappy code. It is also expensive code, for it much more often needs costly maintenance and modification. Yet this is tolerated because that extra expense may be pushed to the next business quarter. Sadly, this has become the American Way.