From: Nick on
Keith Thompson <kst-u(a)mib.org> writes:

> Nick <3-nospam(a)temporary-address.org.uk> writes:
>> Keith Thompson <kst-u(a)mib.org> writes:
>>> Jeff Flinn <TriumphSprint2000(a)hotmail.com> writes:
>>>> Keith Thompson wrote:
>>>>> Jeff Flinn <TriumphSprint2000(a)hotmail.com> writes:
>>>>>> Keith Thompson wrote:
>>>>>>> Seebs <usenet-nospam(a)seebs.net> writes:
>>>>>>>> On 2010-04-28, bart.c <bartc(a)freeuk.com> wrote:
>>>>>>>>> int* findvalue(int value)
>>>>>>>>> {int *a=0;
>>>>>>>>>
>>>>>>>>> for(int xInd = 0; !a && xInd < datasize; ++xInd)
>>>>>>>>> for(int yInd = 0; !a && yInd < dataxindsize; ++yInd)
>>>>>>>>> for(int zInd = 0; !a && zInd < dataxindyindsize; ++zInd)
>>>>>>>>> {
>>>>>>>>> if(data[xInd][yInd][zInd] == value)
>>>>>>>>> a=&data[xInd][yInd][zInd];
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> return a;
>>>>>>>>> }
>>>>>>>> This is noticably slower and less clear. I would not consider it
>>>>>>>> an acceptable rewrite -- but I would consider the other form a good
>>>>>>>> rewrite of it.
>>>>>>> I mostly agree, but -- "noticably slower"? How did you notice?
>>>>>> Certainly is for the case data[0][0][0] == value. ;-)
>>>>>
>>>>> How so?
>>>>>
>>>>> I'm not arguing that it's not *likely* to be slower, but (a) it's not
>>>>> likely to be *noticeably* slower unless the entire loop is executed many
>>>>> many times, and (b) it might not be slower at all, depending on how
>>>>> clever the compiler is.
>>>>>
>>>>> (I'm assuming there's not some semantic difference that I haven't
>>>>> noticed.)
>>>>
>>>> Have you profiled your 'assign address' version versus the previous
>>>> 'return when found' version? For different sizes? The 'assign address'
>>>> version is effectively doing a std::for_each, alway visiting every
>>>> element even if the item found is the 1st one. The 'return when found'
>>>> version is analogous to std::find_if. For the case data[0][0][0] ==
>>>> value, your algorithm is O(N), while find_if, is O(1).
>>>
>>> Please look again. I'm fairly sure that all the versions I've seen in
>>> this thread terminate after finding the item.
>>
>> Not when the item is zero, because that's the "nothing has been found
>> yet" flag, so the search executes to exhaustion even if the item was
>> found hours ago.
>
> No, the loops test for ``!a''. ``a'' is a pointer, initialized to 0
> (NULL). ``data'' is presumably a 3-D array of int. When the item
> is found, a is set to point to the item; even if the item's value
> is 0, a is still non-zero (non-NULL).

Bloody hell, so it is. So I want to look for an integer, so I create a
pointer to look for it, just so that I can I use null as a distinct
signal value.

Of course, "a" was a particularly carefully chosen name to make this
clear.

And that's the better and clearer code? Yeah, right!
--
Online waterways route planner | http://canalplan.eu
Plan trips, see photos, check facilities | http://canalplan.org.uk
From: tonydee on
On Apr 29, 8:51 pm, "Daniel T." <danie...(a)earthlink.net> wrote:
> Nick Keighley <nick_keighley_nos...(a)hotmail.com> wrote:
> > On 28 Apr, 13:59, "Daniel T." <danie...(a)earthlink.net> wrote:
> > >  SG <s.gesem...(a)gmail.com> wrote:
> > > > On 28 Apr., 11:02, Richard Heathfield wrote:
> > > >   Value_t* MyClass::findValue(const Value_t& value)
> > > >   {
> > > >     Value_t* result = 0;
> > > >     for(size_t xInd = 0; !result && xInd < data.size(); ++xInd)
> > > >       for(size_t yInd = 0; !result && yInd < data[xInd].size();
> > > >           ++yInd)
> > > >         for(size_t zInd = 0; !result && zInd < data[xInd]
> > > > [yInd].size();
> > > >             ++zInd)
> > > >         {
> > > >           if(data[xInd][yInd][zInd] == value)
> > > >             result = &data[xInd][yInd][zInd];
> > > >         }
> > > >     return result;
> > > >   }
>
> > > > I don't know about you but I like the first version better. It's more
> > > > concise. I find it easier to see what the loop's doing. Maybe it's
> > > > just me. I guess I'm used to these kinds of loops.
>
> > > Since the sample code is obviously in c++, I would rather see something
> > > like:
>
> > > Iterator it = data.begin()
> > > while(it != data.end() && *it != value)
> > >    ++it;
> > > return it != data.end();
> [snip]
> The point of my example was to show that the problem with Juha's code
> wasn't that it had multiple exits, but rather that it was at the wrong
> level of abstraction and therefore seemed to need multiple exits.
> Richard, removed the multiple exits without fixing the abstraction
> problem and he ended up with a worse result.

That's a very insightful point, and in many ways good practice -
particularly if it prevents client code coupling to the implementation
or layout of your container. In this case though, the find function
might be a member of the container - inside the encapsulation - and
coupling quite acceptable. Further, say you want something just
marginally more complicated - like skipping every second value of
yInd: you can't do that with your iterator (the x/y/z boundaries are
lost), but it would be a trivial modification to the explicit loop.
Localisation, concision, directness and simplicity must be balanced
against abstraction - the latter is not always better. So, while I
agree an iterator may sometimes be good, I strongly disagree that the
explicitly nested loops were inherently at the wrong level of
abstraction, yielding a necessarily worse result.

Cheers,
Tony
From: bart.c on
"Nick" <3-nospam(a)temporary-address.org.uk> wrote in message
news:878w854hmy.fsf(a)temporary-address.org.uk...
> Keith Thompson <kst-u(a)mib.org> writes:

>> No, the loops test for ``!a''. ``a'' is a pointer, initialized to 0
>> (NULL). ``data'' is presumably a 3-D array of int. When the item
>> is found, a is set to point to the item; even if the item's value
>> is 0, a is still non-zero (non-NULL).
>
> Bloody hell, so it is. So I want to look for an integer, so I create a
> pointer to look for it, just so that I can I use null as a distinct
> signal value.

Er, yes. Without being able to jump out of the loops, there aren't too many
alternatives. (Perhaps setting each index to the maximum bound, but in this
example the bound expressions were complex.)

Anyway, in this case, the return value from the function *was* a pointer to
the item.

> Of course, "a" was a particularly carefully chosen name to make this
> clear.
>
> And that's the better and clearer code? Yeah, right!

"a" is for "address"...

The claim was that removing the exit from the inner loop would result in a
"MASSIVE" loss of clarity.

However the for-loop was written in such a way, even my slightly streamlined
version, that a short variable like "a" might well get lost in the noise:

for(size_t zInd = 0; zInd < data[xInd][yInd].size(); ++zInd) /* original */

I if I had to write such a loop, in C-style, and in the context of a 10-line
function called "findvalue", it might look like:

for (z=0; z<zsize; ++z)

with zsize set outside the loops. (But then, I wouldn't be bothered myself
about using return, break or goto.)

--
Bartc

From: Daniel T. on
tonydee <tony_in_da_uk(a)yahoo.co.uk> wrote:
> On Apr 29, 8:51�pm, "Daniel T." <danie...(a)earthlink.net> wrote:
> > Nick Keighley <nick_keighley_nos...(a)hotmail.com> wrote:
> > > On 28 Apr, 13:59, "Daniel T." <danie...(a)earthlink.net> wrote:
> > > > �SG <s.gesem...(a)gmail.com> wrote:
> > > > > On 28 Apr., 11:02, Richard Heathfield wrote:
> > > > >
> > > > > � Value_t* MyClass::findValue(const Value_t& value)
> > > > > � {
> > > > > � � Value_t* result = 0;
> > > > > � � for(size_t xInd = 0; !result && xInd < data.size(); ++xInd)
> > > > > � � � for(size_t yInd = 0; !result && yInd < data[xInd].size();
> > > > > � � � � � ++yInd)
> > > > > � � � � for(size_t zInd = 0; !result && zInd < data[xInd]
> > > > > [yInd].size();
> > > > > � � � � � � ++zInd)
> > > > > � � � � {
> > > > > � � � � � if(data[xInd][yInd][zInd] == value)
> > > > > � � � � � � result = &data[xInd][yInd][zInd];
> > > > > � � � � }
> > > > > � � return result;
> > > > > � }
> > > > >
> > > > > I don't know about you but I like the first version better. It's more
> > > > > concise. I find it easier to see what the loop's doing. Maybe it's
> > > > > just me. I guess I'm used to these kinds of loops.
> > > >
> > > > Since the sample code is obviously in c++, I would rather see something
> > > > like:
> > > >
> > > > Iterator it = data.begin()
> > > > while(it != data.end() && *it != value)
> > > > � �++it;
> > > > return it != data.end();
> > [snip]
> > The point of my example was to show that the problem with Juha's code
> > wasn't that it had multiple exits, but rather that it was at the wrong
> > level of abstraction and therefore seemed to need multiple exits.
> > Richard, removed the multiple exits without fixing the abstraction
> > problem and he ended up with a worse result.
>
> That's a very insightful point, and in many ways good practice -
> particularly if it prevents client code coupling to the implementation
> or layout of your container. In this case though, the find function
> might be a member of the container - inside the encapsulation - and
> coupling quite acceptable. Further, say you want something just
> marginally more complicated - like skipping every second value of
> yInd: you can't do that with your iterator (the x/y/z boundaries are
> lost), but it would be a trivial modification to the explicit loop.
> Localisation, concision, directness and simplicity must be balanced
> against abstraction - the latter is not always better. So, while I
> agree an iterator may sometimes be good, I strongly disagree that the
> explicitly nested loops were inherently at the wrong level of
> abstraction, yielding a necessarily worse result.

In this case, the nested for loops were the wrong level, even if the
code was embedded in a member function. For your, rather arbitrary,
modification they may not be, but even there an iterator that knows how
to skip every second value of yInd might be more appropriate IMHO (if
such a construct is needed once for this container type, it probably is
needed in other places and duplicating the nested for loop construct is
even worse than putting it in once.

That said, finding examples where multiple returns are a good idea is
not hard, even with the code I presented something like this:

for (Iterator it = data.begin(); it != data.end(); ++it)
if (*it == value)
return true;
return false;

Here I have removed the extra conditional by introducing an extra
return. It's not how I would write it, but the above is quite acceptable
IMHO.

There is a general pattern here I think. When there are multiple exit
conditions to a loop (there is an && in the exit condition,) and we have
to be able to distinguish after the loop which condition caused it to
exit, then it may be appropriate to have multiple exits from the loop.
I'm just musing here, but what do you guys think?
From: Nick on
"Daniel T." <daniel_t(a)earthlink.net> writes:

> There is a general pattern here I think. When there are multiple exit
> conditions to a loop (there is an && in the exit condition,) and we have
> to be able to distinguish after the loop which condition caused it to
> exit, then it may be appropriate to have multiple exits from the loop.
> I'm just musing here, but what do you guys think?

I'm very happy with it. My view is that leaving early is perfectly all
right in at least two circumstances:
a) in a "do I do this at all?" check at the start of some code
b) when you've finished what you came to do (like finding something in a
search function).

For (a), I'd much rather see
enum retval do_something(char *s) {
if(s == NULL)
return no_string;
if(!*s)
return empty_string;
if(strlen(s) < 3)
return short_string;
while(*s) {
if(....)
}
return not_found;
}

than something involving a pile of nested ifs and a variable to hold the
eventual return value. This way you *know* that all's over with,
without reading down to the bottom to check.
--
Online waterways route planner | http://canalplan.eu
Plan trips, see photos, check facilities | http://canalplan.org.uk