On 12/28/2013 9:33 AM, Aleksandar Kuktin wrote:
>[...]
> One possible way to solve the survivability error is to backtrack the
> counter.
>
> for (i=0; i<len; i++)
> if (needs_deleting(arr[i])) {
> len = del_at(i, len, arr);
> i--;
> }
>
> The solution proposed by Willem and Eric is obviously better because
> there is infinitelly less copying going on, but I am interested in the
> general opinion on backtracking counters (unless that is also heavily
> dependent on context). Is such a practice considered un-elegant, or
> perhaps something similar?
It doesn't seem to me that "elegance" is a useful criterion
for making decisions about software tactics. Like beauty, elegance
is in the eye of the beholder, and beholders will disagree. I've
heard "elegant" used to describe some stupendous botches, like
moving a file from one directory to another by copying it across
a network to an entirely different system and then copying it back
(true story, I kid you not)!
To my way of thinking, the most important consideration should
be correctness: Does the software (or fragment) do what is desired,
under all circumstances that might arise? Incorrectness, not
inefficiency, was the biggest flaw in the original code. After
that should come clarity and/or elegance, because they help build
confidence in the code's correctness and because they make it easier
to alter and extend. Performance is less important than either,
usually, unless it's so bad that it compromises correctness (the
O.P. spoke of a fifteen-minute wait for his game program to make
its first move; that's probably not "within specification").
So, back to the loop question: IMHO, fiddling with what looks
like a loop counter and/or fiddling with the termination condition
are threats to clarity. If you can recast the loop in a simpler
and more readable form, you should consider doing so. The loop
forms that I think most readable (but remember the "eye of the
beholder" stuff) are
// Counted loops:
for (i = START; i < LIMIT; i += STEP) ...
for (i = LIMIT; (i -= STEP) >= START; ) ...
// Predicated loops:
for (i = START; predicate(i); i += STEP) ...
while (predicate()) ...
Here, I'm supposing that START, LIMIT, and STEP are values that
do not change in the loop; they may not be "constants" in the C
sense, but if they vary while the loop is in progress the code
may well be harder to follow. But there are exceptions even to
this fuzzy guideline; for example a binary search:
for (lo = START, hi = LIMIT; lo < hi; ) {
mid = lo + (hi - lo) / 2;
if (key < array[mid])
hi = mid;
else if (key > array[mid])
lo = mid + 1;
else
return mid; // found it!
}
return -1; // not there (sob!)
Even though *both* participants in this loop's predicate are
subject to change during the loop, I don't think people will
find it confusing. If there's any doubt, perhaps it should be
rewritten as:
lo = START;
hi = LIMIT;
while (lo < hi) ...
Finally, to your "backtracking counter" loop: I really,
really don't like it. A better formulation, I think, would be
for (i = 0; i < len; ) {
if (needs_deleting(arr[i])
len = del_at(i, len, arr);
else
++i;
}
... because the reader will not be fooled by the familiar-looking
`i++' in the first line and perhaps overlook the unfamiliar in-loop
adjustment. Seeing no index adjustment at all in the `for', he
will look inside the loop to learn what happens to `i', and (I
think) his route to understanding will be a shorter one.
--
Eric Sosman
eso...@comcast-dot-net.invalid