From: S Perryman on 17 May 2010 07:03 Juha Nieminen wrote: > Care to show an actual example of your "simpler, cleaner and easier to > follow" version of exiting a nested loop by meddling with the loop > conditions instead of using 'return'? For example, modify the following > code to conform to your specifications: > Value_t* MyClass::findValue(const Value_t& value) > { > for(size_t xInd = 0; xInd < data.size(); ++xInd) > for(size_t yInd = 0; yInd < data[xInd].size(); ++yInd) > for(size_t zInd = 0; zInd < data[xInd][yInd].size(); ++zInd) > { > if(data[xInd][yInd][zInd] == value) > return &data[xInd][yInd][zInd]; > } > return 0; > } Was directed here as I'm known to my colleagues as a "single entry single exit" devotee, and twas said you had an interesting challenge ... 1. A "like for like" equivalent of your example is (braces inserted because that is the convention I use) : Value_t* MyClass::findValue(const Value_t& value) { Value_t* RESULT = 0 ; for(size_t xInd = 0; xInd < data.size(); ++xInd) { for(size_t yInd = 0; yInd < data[xInd].size(); ++yInd) { for(size_t zInd = 0; zInd < data[xInd][yInd].size(); ++zInd) { if(data[xInd][yInd][zInd] == value) { RESULT = &data[xInd][yInd][zInd] ; // Termination states for the xInd/yInd/zInd loops xInd = data.size() - 1 ; yInd = data[xInd].size() - 1 ; zInd = data[xInd][yInd].size() - 1 ; } } } } return RESULT ; } 2. To make the above more acceptable in terms of "Hoare logic" : Value_t* MyClass::findValue(const Value_t& value) { Value_t* RESULT = 0 ; boolean found = false ; size_t x = 0; size_t y = 0; size_t z = 0; for(size_t xInd = 0; xInd < data.size(); ++xInd) { for(size_t yInd = 0; yInd < data[xInd].size(); ++yInd) { for(size_t zInd = 0; zInd < data[xInd][yInd].size(); ++zInd) { if(data[xInd][yInd][zInd] == value) { found = true ; x = xInd ; y = yInd ; z = zInd ; // Termination states for the xInd/yInd/zInd loops xInd = data.size() - 1 ; yInd = data[xInd].size() - 1 ; zInd = data[xInd][yInd].size() - 1 ; } } } } // found OR (x = y = z = 0) // FORALL found : // x < data.size() , y < data[x].size() , z < data[x][y].size() if(found) { RESULT = &data[x][y][z] ; } return RESULT ; } Differences in code size are evident at the source code level. Differences in efficiency are negligible ?? (cost of 5 assignments for #1, 13 assignments + 1 boolean flag test for #2) . #1 does not appear to be that difficult to understand. #2 shows that reasoned correctness (Hoare logic etc) does not come for free. Regards, Steven Perryman
From: Ben Bacarisse on 16 May 2010 12:14 S Perryman <q(a)q.net> writes: > Juha Nieminen wrote: > >> Care to show an actual example of your "simpler, cleaner and easier to >> follow" version of exiting a nested loop by meddling with the loop >> conditions instead of using 'return'? For example, modify the following >> code to conform to your specifications: > >> Value_t* MyClass::findValue(const Value_t& value) >> { >> for(size_t xInd = 0; xInd < data.size(); ++xInd) >> for(size_t yInd = 0; yInd < data[xInd].size(); ++yInd) >> for(size_t zInd = 0; zInd < data[xInd][yInd].size(); ++zInd) >> { >> if(data[xInd][yInd][zInd] == value) >> return &data[xInd][yInd][zInd]; >> } > >> return 0; >> } <snip re-write number 1> > 2. To make the above more acceptable in terms of "Hoare logic" : > > Value_t* > MyClass::findValue(const Value_t& value) > { > Value_t* RESULT = 0 ; > > boolean found = false ; > size_t x = 0; > size_t y = 0; > size_t z = 0; > > for(size_t xInd = 0; xInd < data.size(); ++xInd) > { > for(size_t yInd = 0; yInd < data[xInd].size(); ++yInd) > { > for(size_t zInd = 0; zInd < data[xInd][yInd].size(); ++zInd) > { > if(data[xInd][yInd][zInd] == value) > { > found = true ; > x = xInd ; > y = yInd ; > z = zInd ; > > // Termination states for the xInd/yInd/zInd loops > xInd = data.size() - 1 ; > yInd = data[xInd].size() - 1 ; > zInd = data[xInd][yInd].size() - 1 ; > } > } > } > } > > // found OR (x = y = z = 0) > // FORALL found : > // x < data.size() , y < data[x].size() , z < data[x][y].size() [Aside: in case anyone thinks this is all the loop establishes the key result is that found => data[x][y][z] = value and that !found => forall i, j, k in range data[i][j][k] != value. Depending on what you are using this for you might also want to show that (x, y, z) is the smallest tuple s.t. data[i][j][k] != value.] > if(found) { RESULT = &data[x][y][z] ; } > > return RESULT ; > } <snip> If you twisted my arm, I'd be tempted by: Value_t *MyClass::findValue(const Value_t &value) { size_t xInd = 0, yInd = 0, zInd = 0; while (xInd < data.size() && data[xInd][yInd][zInd] != value) { if (++zInd == data[xInd][yInd].size()) { zInd = 0; if (++yInd == data[xInd].size()) { yInd = 0; ++xInd; } } } return xInd == data.size() ? 0 : &data[xInd][yInd][zInd]; } -- Ben.
From: S Perryman on 16 May 2010 14:00 Ben Bacarisse wrote: > S Perryman <q(a)q.net> writes: >>2. To make the above more acceptable in terms of "Hoare logic" : >> >>Value_t* >>MyClass::findValue(const Value_t& value) >>{ >> Value_t* RESULT = 0 ; >> >> boolean found = false ; >> size_t x = 0; >> size_t y = 0; >> size_t z = 0; >> >> for(size_t xInd = 0; xInd < data.size(); ++xInd) >> { >> for(size_t yInd = 0; yInd < data[xInd].size(); ++yInd) >> { >> for(size_t zInd = 0; zInd < data[xInd][yInd].size(); ++zInd) >> { >> if(data[xInd][yInd][zInd] == value) >> { >> found = true ; >> x = xInd ; >> y = yInd ; >> z = zInd ; >> >> // Termination states for the xInd/yInd/zInd loops >> xInd = data.size() - 1 ; >> yInd = data[xInd].size() - 1 ; >> zInd = data[xInd][yInd].size() - 1 ; >> } >> } >> } >> } >> >> // found OR (x = y = z = 0) >> // FORALL found : >> // x < data.size() , y < data[x].size() , z < data[x][y].size() > > > [Aside: in case anyone thinks this is all the loop establishes the key > result is that found => data[x][y][z] = value Correct. > and that !found => forall > i, j, k in range data[i][j][k] != value. Depending on what you are > using this for you might also want to show that (x, y, z) is the > smallest tuple s.t. data[i][j][k] != value.] Which completes the correctness spec for the search. #1 has an issue, since it uses the same termination state for both successful and unsuccessful searches. Hence the reason for showing #2. Regards, Steven Perryman
From: James Dow Allen on 19 May 2010 07:03 On May 17, 6:03 pm, S Perryman <q...(a)q.net> wrote: > #1 does not appear to be that difficult to understand. Sorry to repeat myself here (although that hasn't stopped some of the rest of you :-) but this whole dialog continues to stagger my imagination! No, #1 isn't "too difficult" to understand ... but it *is* more difficult than the simpler, more obvious approach. Another grown man felt the need to post flowchart comparisons that blithely ignored that (A && B) is more "complicated" than (B). If it isn't clear what I'm driving at then run this code through your "tool": A = B ? C : D; if (B) AA = C; else AA = D; If your tool tells you the first form above is simpler, would you agree the tool is flawed? I am not alone in thinking that the simpler coding is, in this case, clearly best. I can't imagine how to interpret opposition except as "overly dogmatic." Hope this helps, James Dow Allen
From: bart.c on 19 May 2010 08:42
James Dow Allen wrote: > On May 17, 6:03 pm, S Perryman <q...(a)q.net> wrote: >> #1 does not appear to be that difficult to understand. > > Sorry to repeat myself here (although that hasn't stopped > some of the rest of you :-) but this whole dialog continues > to stagger my imagination! > > No, #1 isn't "too difficult" to understand ... but it *is* > more difficult than the simpler, more obvious approach. > > Another grown man felt the need to post flowchart > comparisons that blithely ignored that (A && B) > is more "complicated" than (B). If it isn't clear what > I'm driving at then run this code through your "tool": > A = B ? C : D; > > if (B) > AA = C; > else > AA = D; > If your tool tells you the first form above is simpler, > would you agree the tool is flawed? AA? Whether A or AA, this potentially complex term has to be repeated. This form also encourages you to spread it over 4 lines instead of 1, which can have the effect of expanding the surrounding statement or function enough to make it harder to take in at a glance. -- Bartc |