J
Juha Nieminen
Type1 foo(Type2 value)
{
for(size_t i = 0; i < data.size(); ++i)
for(size_t j = 0; j < data.size(); ++j)
for(size_t k = 0; k < data[j].size(); ++k)
if(meetsRequirements(data[j][k], value)
return data[j][k];
}
Your function returns garbage when nothing meets the requirements.
Most compilers will issue a warning, though.
A stuctured solution to your example could use a boolean flag which
is changed when the data is found.
Exactly how is that different from 'break'? (Let's assume a 'break'
which can take as parameter how many nested loops it breaks from, like
some languages have. In other words, you could do a "break 3;" in the
example above, assuming it was supported.)
The only difference I can see is that the 'break' would be
compiler-supported (and thus easier for the compiler to optimize), while
the boolean solution is user-written, which is more work and clumsier.
Also, I don't believe the boolean solution would make the code any
easier to understand and follow than the 'break' solution.
// Find the first value in 'data' which meets the requirements
// imposed by the parameter:
Type1 foo (Type2 value)
{
int search = 1;
Type1 result = Type1default;
for (size_t i = 0; i < data.size() & search; ++i) {
for (size_t j = 0; j < data.size() & search; ++j) {
for (size_t k = 0; k < data[j].size() & search; ++k) {
if (meetsRequirements(data[j][k], value) {
result = data[j][k];
search = 0;
}
}
}
}
return result;
}
Another point: Assume that initializing the return value is a very
heavy operation. If a result is found, you are initializing the return
value twice, for no reason. (If the found value was returned from inside
the loop, and if the loops end without result, the default value is
returned, the return value would then always be initialized only once.)
In fact, it would be enough for the default value to be very heavy to
construct for this problem to happen.
Sure, you could add yet another boolean to take care of that... Is
this supposed to make the code easier to write and read, and/or less
error-prone?
As you can see, I prefer to use curly braces even when they are not
necessary.
That's mostly a matter of style and preference. I omitted them mostly
for brevity (ie. it doesn't mean I consistently avoid the braces in real
code whenever I can).
Btw, why do you use the braces in an inconsistent way?
In a language which has no C for loops like Seed7 the function
would look as follows:
const func Type1: foo (in Type2: aValue) is func
result
var Type1: result is Type1.value;
local
var integer: i is 1;
var integer: j is 1;
var integer: k is 1;
var boolean: search is TRUE;
begin
while i <= maxIdx(data) and search do
while j <= maxIdx(data) and search do
while k < maxIdx(data[j]) and search do
if meetsRequirements(data[j][k], aValue) then
result := data[j][k];
search := FALSE;
end if;
incr(k);
end while;
incr(j);
end while;
incr(i);
end while;
end func;
I'm sorry but I have to confess that doesn't look like very attractive
to me. The 9 lines of code in my original function (with the fix of
returning the default value at the end) vs. 23 lines in your example. I
honestly can't say your version is more readable either.
Let's make it clear that I'm all against compact obfuscated code.
However, there's a limit where code becomes *too* verbose to be comfortable.
I know that this solution is not as elegant.
Maybe I should introduce an advanced version of the 'for' statement
like:
for i range minIdx(data) range maxIdx(data) andWhile search do
What do you think?
A 'for' clause is always a handy shortcut for 'while', so why not.
I also think you should really consider 'break' (perhaps even the
version which takes a parameter, as I mentioned above).