Reply by Don Y July 1, 20112011-07-01
Hi Steve,

On 7/1/2011 1:28 PM, steve wrote:

> ha, I deal with those "simplifiers" everyday, simplifying sometimes > removes much insight and physical meaning to the algorithm your trying
It depends on *how* (and what) you are simplifying. For example: if ( (man) || (woman && pregnant) ) *clearly* is as easy to understand as if ( man || pregnant ) If you look at a lot of conditionals, particularly, you can see where the person writing it just didn't *think* about what he was doing. It's as if he laid out each individual possibility "in a vacuum" without concern for the stanza of code immediately preceding, etc. Granted, some compilers can remove this redundancy. But, it's presence increases the chance of typographic errors creeping in (e.g., "if (sex = MAN)" instead of "if (sex == MAN)" or "if (sex != WOMAN)", instead) as well as adding a lot to clutter. I *really* wish we had multicolored parens (no, not *colorized* but actually inherently different "characters" -- like brace, bracket, etc.) to make it easier to write and parse complex conditionals. [which is why I resort to the style of the first example]
> to implement. A algorithm that natually has 8 logical steps to it > should be left with those 8 steps, even if it can be reduced to 4 > cryptic steps and still give you the same answer. Because when that
Agreed. Unless those steps are obvious in the resulting code. E.g., foo += 4 vs four instances of foo++;
> algorithm needs to be modified or debugged those 4 steps are going to > have to unrolled into those 8 steps. I believe it's a carryover from > the olden days when saving every byte of source code counted. C > language itself is a perfect example of that mentality, trading too > much simplification for readability.
I always found: "Make everything as simple as it can be -- but no simpler" an amusing axiom. (on a par with "You always find the thing you seek in the *last* place you look")
Reply by Steve at fivetrees July 1, 20112011-07-01
On 01/07/2011 21:28, steve wrote:
> ha, I deal with those "simplifiers" everyday, simplifying sometimes > removes much insight and physical meaning to the algorithm your trying > to implement. A algorithm that natually has 8 logical steps to it > should be left with those 8 steps, even if it can be reduced to 4 > cryptic steps and still give you the same answer. Because when that > algorithm needs to be modified or debugged those 4 steps are going to > have to unrolled into those 8 steps. I believe it's a carryover from > the olden days when saving every byte of source code counted. C > language itself is a perfect example of that mentality, trading too > much simplification for readability.
I can only agree. I'm not arguing for logic reduction for its own sake; if it hinders readability, fergin fergit it. However, I see code (regularly) that is *way* more complex than it needs to be, simply because of a lack of factoring - there's loads of duplicate code (and testing) because the coder is looking for conditions within conditions within conditions, and then those same conditions within conditions within *other* conditions, all of which could have been reduced to a few *clearer* set of tests by way of e.g. a bit of thought and a Karnaugh map... Steve -- http://www.fivetrees.com
Reply by steve July 1, 20112011-07-01
On Jun 28, 12:41=A0am, Arlet Ottens <usene...@c-scape.nl> wrote:
> On 06/28/2011 02:33 AM, Steve at fivetrees wrote: > > > > > > >> I use goto whenever it results in clearer and more readable code. Usin=
g
> >> "goto error" in a function that ends like this: > > >> error: > >> cleanup_stuff(); > >> return retval; > >> } > > >> is easy to understand. The 'error' label makes it clear what the purpo=
se
> >> is, even without reading any further, and it helps to keep the mainlin=
e
> >> code free of error handling. It may be possible to rewrite the code to > >> avoid the 'goto', but if that doesn't result in any tangible benefits, > >> why bother ? > > > There are any number of ways of avoiding this. One is to have an error > > flag; set it if you need to do cleanup (not great). Another is to retur=
n
> > FALSE early if errors are encountered (see below; preferred). > > Except that 'return FALSE' doesn't work in the case above where you also > need to call 'cleanup_stuff()' unless you duplicate the cleanup code, > which is even uglier than error flags. > > >> I never worry about simplifying the logic, unless it helps readability=
.
> > > Friend, I lost you right there. *Simplifying the logic can only help > > readability*. > > Sometimes simplying the logic can help to increase readability, > sometimes it doesn't. For the same reason, I sometimes write > > if( p ) > > and at other times I write: > > if( p !=3D 0 ) > > Sometimes I write: if( a && b ), and sometimes I write: > > if( a ) > =A0 =A0 if( b ) > > It all depends on the circumstances, and "how it reads". It's not > necessary the case that the simplest logic is the easiest to read, > although usually it helps, and in that case I obviously simplify the logi=
c. ha, I deal with those "simplifiers" everyday, simplifying sometimes removes much insight and physical meaning to the algorithm your trying to implement. A algorithm that natually has 8 logical steps to it should be left with those 8 steps, even if it can be reduced to 4 cryptic steps and still give you the same answer. Because when that algorithm needs to be modified or debugged those 4 steps are going to have to unrolled into those 8 steps. I believe it's a carryover from the olden days when saving every byte of source code counted. C language itself is a perfect example of that mentality, trading too much simplification for readability.
Reply by Don Y June 29, 20112011-06-29
On 6/28/2011 4:05 PM, Steve at fivetrees wrote:

> I should know better. I'm working with a chap (hi, Mike!) whose work I > respect hugely. His code and my code will likely never approve of one > another ;).
[fanfare] *Core Wars*!!!
Reply by Don Y June 28, 20112011-06-28
On 6/28/2011 4:02 PM, Steve at fivetrees wrote:
> On 28/06/2011 05:17, Don Y wrote: >> Of course, unlike your 88%RH, we're sitting at something like *seven* %. > > Ah. > > Where are you?
Arizona, USA.
> Steve > (Will respond to rest of hugely interesting/challenging post when/if I > get time.)
never mind that -- get back to WORK!!! (slacker...) :>
Reply by Steve at fivetrees June 28, 20112011-06-28
On 28/06/2011 05:41, Arlet Ottens wrote:
> > > I suspect you're fairly new to this game (which I'm not) > > Yeah, I guess 25 years is fairly new.
Ah. Consider humble pie consumed ;). I should know better. I'm working with a chap (hi, Mike!) whose work I respect hugely. His code and my code will likely never approve of one another ;). Steve -- http://www.fivetrees.com
Reply by Steve at fivetrees June 28, 20112011-06-28
On 28/06/2011 05:17, Don Y wrote:
> Of course, unlike your 88%RH, we're sitting at something like *seven* %.
Ah. Where are you? Steve (Will respond to rest of hugely interesting/challenging post when/if I get time.) -- http://www.fivetrees.com
Reply by Arlet Ottens June 28, 20112011-06-28
On 06/28/2011 02:33 AM, Steve at fivetrees wrote:

>> I use goto whenever it results in clearer and more readable code. Using >> "goto error" in a function that ends like this: >> >> error: >> cleanup_stuff(); >> return retval; >> } >> >> is easy to understand. The 'error' label makes it clear what the purpose >> is, even without reading any further, and it helps to keep the mainline >> code free of error handling. It may be possible to rewrite the code to >> avoid the 'goto', but if that doesn't result in any tangible benefits, >> why bother ? > > There are any number of ways of avoiding this. One is to have an error > flag; set it if you need to do cleanup (not great). Another is to return > FALSE early if errors are encountered (see below; preferred).
Except that 'return FALSE' doesn't work in the case above where you also need to call 'cleanup_stuff()' unless you duplicate the cleanup code, which is even uglier than error flags.
>> I never worry about simplifying the logic, unless it helps readability. > > Friend, I lost you right there. *Simplifying the logic can only help > readability*.
Sometimes simplying the logic can help to increase readability, sometimes it doesn't. For the same reason, I sometimes write if( p ) and at other times I write: if( p != 0 ) Sometimes I write: if( a && b ), and sometimes I write: if( a ) if( b ) It all depends on the circumstances, and "how it reads". It's not necessary the case that the simplest logic is the easiest to read, although usually it helps, and in that case I obviously simplify the logic. > I suspect you're fairly new to this game (which I'm not) Yeah, I guess 25 years is fairly new.
Reply by Don Y June 28, 20112011-06-28
Hi Steve,

On 6/27/2011 5:33 PM, Steve at fivetrees wrote:
> To expand my earlier vowel-heavy response:
(sigh) I'd have rather watched Vanna twirl some consonants...
>>> The point here is that there are several places where "goto win" >>> would have cleaned up the code. The do-while hack runs the >>> risk of not being familiar to novices (and, also gives trouble >>> if you want to "break break" -- requiring a flag and some >>> kludge logic). And, the do-while cheats me out of three >>> characters per line (because it introduces another indent level) >> >> I use goto whenever it results in clearer and more readable code. Using >> "goto error" in a function that ends like this: >> >> error: >> cleanup_stuff(); >> return retval; >> } >> >> is easy to understand. The 'error' label makes it clear what the purpose >> is, even without reading any further, and it helps to keep the mainline >> code free of error handling. It may be possible to rewrite the code to >> avoid the 'goto', but if that doesn't result in any tangible benefits, >> why bother ? > > There are any number of ways of avoiding this. One is to have an error > flag; set it if you need to do cleanup (not great).
Booo! Then every conditional after that has to include "!error &&" (assuming you *don't* want to execute the code protected by the conditional if you've previously detected an error)
> Another is to return > FALSE early if errors are encountered (see below; preferred).
But, if error means some form of *recovery* is required, that means you have to wrap this portion of the code in a separate function -- so that the caller can examine that error return and unwind things, as appropriate. If doing that requires accessing something referenced *within* the called function (the one that "error returned"), then you have to make that data accessible to the calling function, etc.
> For me, using a goto is an admission of failure (to find a cleaner way), > and I've not done this in 30+ years. There is *always* a better, > cleaner, more comprehensible, and more robust way.
I don't think I would agree with that "unconditionally". I figure Ritchie, et al are pretty smart folks and didn't leave that feature in the language "casually". OTOH, when they created Limbo, the noticeably *removed* the "goto". OTOOH, in creating Limbo, the gave "continue" and "break" an optional argument -- an identifier indicating *which* enclosing "loop" is being referenced (in effect, where to "go to" as a result of the statement's execution). [this must give the MISRA folks fits! continue, break *and* goto all cohorting together!! :> ] I don't think anyone is questioning the *need* for "goto". But, rather, that ruling it out can make code clumsier (e.g., the Limbo break/continue fits my style of wrapping complex conditionals in do-while()'s and breaking, accordingly)
>> The extra indentation shouldn't be a problem, though. If you need more >> than 3-4 levels of indentation, you should probably restructure the code >> and/or use some extra functions anyway. > > I have a code-purity fascist at work ;). I love him dearly, as he's even > more OCD than me. If I need a sanity check on something I'm doing, I run > it past him. If he were to object to something I'm doing, it would make > me think hard... but this hasn't happened yet. > > So... recently I ran something I do a lot past him. I had validation > code that (simplified) looked like this: > > if ( ! first_bit_of_data_valid() ) > return( FALSE ); > if ( ! second_bit_of_data_valid() ) > return( FALSE ); > ... some processing... > if ( ! processing_happy ) > return( FALSE ); > return( TRUE ); > > These "early returns" get objected to (by n00bs, mainly) on the basis > that some see them as glorified gotos; I don't. I see them as > structurally sound, since they all wind up in the same place (a return).
This is equivalent to the example I posted. The problem comes in when what you really want is: if ( ! first_bit_of_data_valid() ) { cleanup_mess_from_first_bit(params1); return( FALSE ); } if ( ! second_bit_of_data_valid() ) { cleanup_mess_from_second_bit(params2); cleanup_mess_from_first_bit(params1); return( FALSE ); } etc. Sure, you can interpose another function to catch the "FALSE" return value... but, that function needs to be able to access all the stuff that the "cleanup" routines access -- potentially in the context of the "false-returning function". At some point, this just gets clumsy and forces you to go out of your way *just* to avoid a goto.
> Consider for a second the indentation levels of multiple tests for > validity without early returns - every time you do a test, you indent > one more time. (I have worse examples: imagine maintaining time of day, > then date.)
Exactly. This is the case with the arbitrary precision decimal arithmetic package I alluded to previously. The code isn't overly complex -- "tedious" is a better term (lots of care to be sure you don't have fence-post errors, etc.). Couple that with my preference for informative (i.e., *long*) identifiers and every level of indent starts to hurt. Especially as it forces same-line comments further and further out (making them shorter and less effective -- hence my preference for comment blocks)
> So anyway, my OCD colleague thoroughly approved ;). > >>>> Similarly, I've seen a lot of code that is absolutely *infested* with >>>> if/then/elses, where my background in hardware (Karnaugh maps, De >>>> Morgan's theorem) has allowed me to simplify it to a few logical cases. >>> >>> <grin> I find I have to leave notes for folks explaining odd >>> choices of conditionals. E.g., >>> if (! ((*ptr != 'e') && (*ptr != 'E')) ) { >>> though that would be a bad choice, here. >> >> I never worry about simplifying the logic, unless it helps readability. > > Friend, I lost you right there. *Simplifying the logic can only help > readability*. I suspect you're fairly new to this game (which I'm not) > and have a few things yet to learn (as do we all). That's cool; don't be > offended and try to keep an open mind about better ways. I've spent most > of my career trying to find ways of keeping out of trouble (and being > able to sleep nights) - that doesn't make my approach perfect (e.g. I > avoid C++ at all costs, much to the scorn of my code-purity fascist > colleague [I prefer good *design* over good *language*), but I do sleep > nights [1].
I will often rewrite conditionals to exploit things that I know to be true of the environment in which the code will operate. E.g., something that is more likely to affect a conditional one way or the other might get "promoted" to the front of the expression -- or, pulled into a separate expression "enclosing" the other expression (to emphasize that it is more significant to the algorithms performance). It's a minor efficiency hack but often can have rewards in loops, etc.
> Steve > [1] Except tonight. Too bloody hot. Am sitting by the aircon.
Pish! 30C? It's *40*C at 9P, here. High of 45C today. And tomorrow. (and I plan on being outdoors much of tomorrow :< ) Of course, unlike your 88%RH, we're sitting at something like *seven* %.
Reply by Steve at fivetrees June 27, 20112011-06-27
To expand my earlier vowel-heavy response:

On 25/06/2011 11:49, Arlet Ottens wrote:
> On 06/25/2011 10:39 AM, Don Y wrote: > >> The point here is that there are several places where "goto win" >> would have cleaned up the code. The do-while hack runs the >> risk of not being familiar to novices (and, also gives trouble >> if you want to "break break" -- requiring a flag and some >> kludge logic). And, the do-while cheats me out of three >> characters per line (because it introduces another indent level) > > I use goto whenever it results in clearer and more readable code. Using > "goto error" in a function that ends like this: > > error: > cleanup_stuff(); > return retval; > } > > is easy to understand. The 'error' label makes it clear what the purpose > is, even without reading any further, and it helps to keep the mainline > code free of error handling. It may be possible to rewrite the code to > avoid the 'goto', but if that doesn't result in any tangible benefits, > why bother ?
There are any number of ways of avoiding this. One is to have an error flag; set it if you need to do cleanup (not great). Another is to return FALSE early if errors are encountered (see below; preferred). For me, using a goto is an admission of failure (to find a cleaner way), and I've not done this in 30+ years. There is *always* a better, cleaner, more comprehensible, and more robust way.
> The extra indentation shouldn't be a problem, though. If you need more > than 3-4 levels of indentation, you should probably restructure the code > and/or use some extra functions anyway.
I have a code-purity fascist at work ;). I love him dearly, as he's even more OCD than me. If I need a sanity check on something I'm doing, I run it past him. If he were to object to something I'm doing, it would make me think hard... but this hasn't happened yet. So... recently I ran something I do a lot past him. I had validation code that (simplified) looked like this: if ( ! first_bit_of_data_valid() ) return( FALSE ); if ( ! second_bit_of_data_valid() ) return( FALSE ); ... some processing... if ( ! processing_happy ) return( FALSE ); return( TRUE ); These "early returns" get objected to (by n00bs, mainly) on the basis that some see them as glorified gotos; I don't. I see them as structurally sound, since they all wind up in the same place (a return). Consider for a second the indentation levels of multiple tests for validity without early returns - every time you do a test, you indent one more time. (I have worse examples: imagine maintaining time of day, then date.) So anyway, my OCD colleague thoroughly approved ;).
>>> Similarly, I've seen a lot of code that is absolutely *infested* with >>> if/then/elses, where my background in hardware (Karnaugh maps, De >>> Morgan's theorem) has allowed me to simplify it to a few logical cases. >> >> <grin> I find I have to leave notes for folks explaining odd >> choices of conditionals. E.g., >> if (! ((*ptr != 'e') && (*ptr != 'E')) ) { >> though that would be a bad choice, here. > > I never worry about simplifying the logic, unless it helps readability.
Friend, I lost you right there. *Simplifying the logic can only help readability*. I suspect you're fairly new to this game (which I'm not) and have a few things yet to learn (as do we all). That's cool; don't be offended and try to keep an open mind about better ways. I've spent most of my career trying to find ways of keeping out of trouble (and being able to sleep nights) - that doesn't make my approach perfect (e.g. I avoid C++ at all costs, much to the scorn of my code-purity fascist colleague [I prefer good *design* over good *language*), but I do sleep nights [1]. Steve [1] Except tonight. Too bloody hot. Am sitting by the aircon. -- http://www.fivetrees.com