I've been an almost exclusively C# programmer for the last 6 years. I'm now working on a project where C++ is the language of choice, and STL is our library for collections.
After using C#'s LINQ, I'm having a very hard time getting into an STL frame of mind.
As an example: Write an equivalent of IEnumerator.Select.
C#
public static IEnumerator<Output> Select(this IEnumerator<Input> input, Func<Input, Output> func) {
while (input.MoveNext) {
yield return func(input.Current);
}
}
Super easy.
Now try writing something similar in C++ and STL. (Setting aside the issue of the convenient syntax of of the yield keyword and anonymous functions).
It can't even be done without first answering a number of difficult questions. Because STL enumerators use inter-enumerator comparisons instead of MoveNext, you have to decide what your enumerator's terminal value will be. Then you have to mess with all the iterator_traits nonsense. STL uses compile-time template dispatch instead of runtime dynamic dispatch, so your iterator has to be templated not just on the value_type of the input enumerator, but also the specific kind of input enumerator.
Don't even get me started on the time I tried to write a map-join iterator in STL.
From looking at the code that other people write, I've come to the conclusion that STL, unaugmented with Boost, is rarely used for anything other than collections and sort.
My proximal observations are:
More generally, I've noticed a few things that clash with my accustomed manner of thinking:
transform_iterator
should be pretty easy - Mooing Duck 2012-04-04 18:42
My real difficulty is that STL seems to make it cumbersome to write my own algorithms - Kennet Belenky 2012-04-04 18:48
Here are two things that I have brought up: 1) Is there a way to succinctly write a mutation iterator in STL? 2) How do you succinctly sort-copy one collection into another - Kennet Belenky 2012-04-04 18:54
std::transform
, except that transform
produces results immediately, where select
creates an object that produces results on demand. Is that difference what leads to your difficulty? If not, what is it - Jerry Coffin 2012-04-04 18:59
IEnumerator<Output>
but appears to return Output
. STL aside, you should fix that. (If I'm mistaken, forgive my ignorance, my C# is poor. - Mooing Duck 2012-04-04 19:22
Some of your question doesn't really make a lot of sense to me. Just for example, you talk about having to deal with "enumerator_traits". I'm not quite sure what you're talking about. Maybe you meant iterator_traits
? I don't recall having having used anything called an "enumerator_trait", nor can I find any mention of such a thing in the C++ standard.
iterator_traits
at least exist, but they're something I rarely "mess with". I'm peripherally aware of their existence, but little more than that. I've written a fair number of iterators and algorithms without ever doing anything specific with iterator_traits
in any of it.
Let's get to the specific question of creating a new collection that's a sorted version of another collection. This is fairly easy in a number of different ways. std::partial_sort_copy
can certainly do that:
#include <algorithm>
#include <vector>
#include <iterator>
#include <iostream>
int main() {
std::vector<int> input;
// generate some data to sort
std::generate_n(std::back_inserter(input), 20, rand);
// a destination for the sorted data:
std::vector<int> result(input.size());
// do the sort/copy:
std::partial_sort_copy(input.begin(), input.end(),
result.begin(), result.end());
// show the sorted data:
std::copy(result.begin(), result.end(),
std::ostream_iterator<int>(std::cout, "\n"));
return 0;
}
For many purposes, however, it's easier to make a copy, then sort:
std::vector<int> result(input.begin(), input.end());
std::sort(result.begin(), result.end());
If you really want it succinct, you can make a copy into a data structure that's innately sorted:
std::multiset<int> result(input.begin(), input.end());
This last, however, usually trades off a little efficiency to make the code shorter. Under many (most?) circumstances that's not a problem, but if you find it too slow, faster alternatives are easily available.
std::vector<in_iter>
- Mooing Duck 2012-04-04 19:44
std::transform
them all at once - Mooing Duck 2012-04-04 20:17
make_tuple
, make_shared
, and others - Mooing Duck 2012-04-04 21:12
IEnumerator.Select
correctly, we call thatboost::transform_iterator
. If I understandboost::zip_iterator
, that's your map-join iterator - Mooing Duck 2012-04-04 18:38