STL rocks
I've just been writing a small article on the "gems of STL" and found that I really like what I'm seeing. A personal favorite is the transform operation (found in the functional header file). For example, lets do some adding.
#include <functional>So, the transform method takes three iterators and an functor. The operators are, from the left, the starting point of the input, the end point of the input and the starting point of the output. The function simply specifies what to do with each list entry to create the output. In this case, std::plus takes two arguments, but we bind the second argument to 2. What this does is that it adds 42 to every item in the list (between begin and end), and replaces the original items (the results are placed from begin and onwards).
#include <list>
...
{
std::list<int> list;
list.push_back( ... ); // Populate
std::transform( list.begin(), list.end(), list.begin(), std::bind2nd( std::plus<int>(), 42 ) );
}
If you want the results to end up in another list, just use the back_inserter magical interator and point it to your results list.
{
std::list<int> list, res;
list.push_back( ... ); // Populate
std::transform( list.begin(), list.end(), std::back_inserter( res ), std::bind2nd( std::plus<int>(), 42 ) );
}
Quite readable and really cool code if you ask me.
12 Comments:
Granted, that's a nice bit of flexibility from a library rather than built into the core.
BUT... Dude, this is horrible syntax!
In python, you would do something like:
lst = [ 1,2,3 ] # declare and populate
lst = [ i + 42 for i in lst ] # add 42 to all elements
These for statements do work on any class that provides iterators.
Or, if you really need a function:
l, r = [], [] # declare two lists
l.append(...) # populate
def myop(a):
return a + 42
r = [ myop(i) for i in l ] # generate new list and populate from call results
Obviously, you have callable classes and all the other goodies you need. Ruby and D would make it similarly easy, I think.
I used to like compiled languages, and especially C. C++ was OK before it started getting crazy with STL and all. These days though, I tend to think it should just die.
Well, I like STL and its generality, but at least for this simple case I find this much more readable ;) :
foreach(int i, list)
result << i + 42;
You can also use std::transform and all of the STL algorithms on Qt containers too. That just works :-)
Anyways, the code would be more readable with a lambda function (C++0x feature)
Hah. You call that readable?
Here's the equivalent code in D, another C-like, compiled language, using the Tools extension library.
list = list /map/ (int i) { return i+42; };
Or alternatively
auto res = list /map/ ex!("a -> a+42");
--FeepingCreature, D fanboy ^^
In haskell you could do it with
map (+2) list
or, using list comprehension,
[x+2 | x <- list]
"Anyways, the code would be more readable with a lambda function (C++0x feature)"
Unfortunately, for every piece of crazy syntax C++0x fixes, it adds two new pieces of random keyword/punctuation madness.
I think is a great post about a good way of using STL for C++. Most of the comments above are about other languages... that wasn't the idea of the post! For C++ and for STL in particular, is not so "horrible syntax" after all.
I'm quite amused by you definition of readable :) Sure technically this is cool, but reading such code is IMHO just horrible, if you're someone who doesn't use the most obscure STL features on a daily basis. back_inserter?? bind2nd?? .. erm, sure... And after what I've read about C++0x, this will get much worse...
with c++0x you will have serveral options:
std::transform( list.begin(), list.end(), list.begin(), [](int i){ return i+42; });
for(int& i : list) i += 42;
std::for_each( list.begin(), list.end(),
[](int & i) { i += 42; });
std::transform( list.begin(), list.end(), list.begin(), bind(plus<int<(), 42, _1));
and... btw, initializer-lists will allow you to init your list like this:
std::list<int> list = { 1, 2, 3, 4 };
alternatively, of course, imagine:
for(int i : { 1, 2, 3, 4 }) list.push_back(i);
and finally list.push_back(1, 2, 3, 4, 5);
(yeah, you get type-safe variadic (template)-functions/classes!)
Variadic templates, (limited) initializer lists, rvalue references and move semantics can be already tested with GCC4.4 trunk. All this but initializer-lists can be already tested wth gcc4.3 given the --std=c++0x option
If you like that (and the concept is damn neat) you should look into specifically designed functional languages, assuming you haven't already. They're designed so that the syntax for that kind of thing is the best it can be.
My favourite's Haskell, but that might be a bit of a culture-shock compared to C++. Ocaml might be a good one to start with, or some lisp if you're not put off by the style.
I personally find it Hilarius that both the python and the D fans posted code much less readable than the original post's. Apparently their ultra readable codes depend on quite obscure or overrated syntax additions, [i+42 for i in lst] and not to mention the D example: list /map/ ex!("a -> a+42"); So D adds a trillion more operators? fun...
So the STL implements this and didn't require the compiler to add random syntax candy. I find it great. If you have doubts, most of the apparent difficulty to read comes from not declaring the functor manually.
But if you would really like to make a convincing python example:
>>> V=[1,2,3]
>>> map(lambda(x):x+42,V)
Post a Comment
<< Home