Thursday, January 05, 2006

Functional Programming Observation Continues...

A follow-up to my previous post on functional programming in C++.
Compare

size_t total = 0;
for(size_t i = 0; i < elements_size(), ++i)
total += elements_[i].bytes();
to

size_t total = 0;
std::for_each(elements_.begin(), elements_.end(), count_bytes(total));
return total;

Neither one is "right."

What I want is to hide the details of iterating through a collection while revealing what is actually being done. In fact, I want to say:

size_t total = 0;
for (each element in elements_)
total += element.bytes();
(Python anyone?)

The "for-loop" approach fails on the "hide the iteration" criteria [although I've been using for loops for so long that for(size_t i = 0; i < elements_size(), ++i) is a single conceptual chunk for me.]

The "for-each" approach also fails to hide the iteration {although familiarity with STL makes begin() ... end() into a single conceptual chunk, also.]

The "for_each" approach also hides what's actually being done -- it depends on a descriptive name (count_bytes is not bad) to provide a hint.

The "for-loop" approach shows the actual work (which is good, but clutters it up with a leftover detail from the indexing process ("elements_[i]" rather than simply "element")

I wonder if there's some way to convince the compiler to recognize:

size_t total = 0;
while(each(element, elements_))
total += element.bytes();
for any arbitrary collection (elements_) of data type (element). This would of course require that the collection obey STL rules.

3 comments:

Anonymous said...

I'll bite...

namespace ahm {
template<typename C> //C is for container
bool each(typename C::const_iterator& iter, const C& container) {
if(iter == 0) iter = container.begin();
else ++iter;
if(iter == container.end()) return false;
return true;
}
}
namespace {
struct Widget {
int bytes_;
int bytes() const { return bytes_; }
};
int countem(const std::vector<Widget>& elements) {
using ahm::each;
size_t total = 0;
std::vector<Widget>::const_iterator iter = 0;
while(each(iter, elements)) {
total += iter->bytes();
}
return total;
}
void test() {
//as before...
}
}

Dale Wilson said...

Interesting, Adam. I'll play with it and maybe adopt it.

Minor quibble:
Rather than:
template<typename C> //C is for container

Why not just:
template<typename Container>

It's only in text books by mathemeticians that the template parameter names need to be one character long ;-)

Dale

Anonymous said...

I would have certainly used Container in real code but I was feeling lazy when writing up the sample (I guess it would have only been two more "Containers" but I didn't realize it at the time).

One option I considered was making the client set the iterator to the begin() point instead of using zero. The use of zero strikes me as a little odd and maybe non-standards-conforming.

For real code you'd want a non-const overload as well.