Re: Job Interview, Did I Mess Up?
brangdon@cix.co.uk (Dave Harris) wrote:
daniel_t@earthlink.net (Daniel T.) wrote (abridged):
You absolutely should treat functions that have side effects
differently than functions that don't, and you need to know which
are which.
You /can/ treat functions differently if they have no side effects,
but I don't agree you necessarily should.
I'm not sure how to respond here. To my mind, procedures are absolutely
different than functions even if the language doesn't formally recognize
the distinction.
And it's not just about side effects. Storing the result in a local
also makes sense if the function can return different results for the
same call, or because it is slow to compute. For example, if char
happens to be a Unicode type, then tolower() would well be quite
expensive. Storing the result is the safest option which requires
least special knowledge.
I said earlier, that creating the temp works well as a performance
optimization (I even used one in sample code to replace repeated calls
to strlen.) Of course we need "special knowledge" in order to know where
to insert performance optimizations.
I don't have strong feelings about whether or not the local should be
used. I do have fairly strong feelings about whether someone should be
criticised for using one.
My main point here was that contrary to accusations made by others,
eschewing the use of the additional temp does *not* break DRY, it is not
a design mistake. If anything, it's the other way around (adding the
temp breaks DRY but is necessary sometimes for performance.) In other
words, I hold the same view here that Dijkstra expressed in his paper
"Notes on Structured Programming":
In my understanding of programs I want such additional variables that
sore redundant information, to be clearly recognized as such... I am
strongly inclined to view such programs as, say, optimizing refinements
of a more abstract program... (pg 55)
I find it interesting that on the one hand, I am being lambasted for
not adding extra variables in a function even though what they
represent is already expressed, while on the other (below,) I'm
being lambasted for putting in a variable that actually represents
something that isn't expressed any other way.
The difference is that the former case isn't adding mutable state.
It's at most another name for the function result. (This would be
clearer if we added a "const", but I wouldn't bother.)
And a "result" variable is expressing what was already expressed by
the code position.
[2]
bool found = false;
while (first != last && !found)
found = (*first++ == value);
return found;
[...]
If I were required to write a function like your example (I
actually did write one recently,) my instinct would be to
write it like example 2.
I didn't expect that, because earlier you said that you wouldn't
introduce a framework of flags, and variables like "found" are what I
think Balog Pal meant by that.
Balog may indeed consider that a flag, I don't consider "found" a flag;
it represents the "results of the function so far."
Your comment above about the "results so far" being expressed by the
code position struck me as interesting. I'll have to mull on that one
for a bit, maybe I will change my tune. :-) However, this still leaves
open the issue of where to check the post-condition. Having multiple
returns means we have to check the post-condition multiple times (either
directly in the code via an assertion or mentally while scanning the
program text.) In cases where the post-condition is trivial, that isn't
much of an issue, but what about when the post-condition is *not*
trivial?
Your answer to this question so far is to create a second function who's
job is to check the result of the first one and then return it that
result...
Here you have written two functions (contains and contains_inner)
that have the exact same preconditions and postconditions.
Two functions that do exactly the same thing, with the
same algorithm, in the same program doesn't sound like a good
idea to me.
They have different purposes; they have no code in common. It's really
not that uncommon to have one layer that checks arguments and another
layer that does the work. It's a clean separation of concerns.
If the NDEBUG flag is defined, your "contains" function is literally no
different than your "contains_inner" function.
What if someone uses the primary function without the checking function?
If the primary function is hidden such that only the checking function
can call it, then what have we gained by adding a function that isn't
useful otherwise?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]