类似于C ++ LINQ的迭代器操作 [英] C++ LINQ-like iterator operations
问题描述
由于受到Linq的污染,我不愿放弃.但是,对于某些事情,我只需要使用C ++.
Having been tainted by Linq, I'm reluctant to give it up. However, for some things I just need to use C++.
作为linq消费者(即对我而言),linq的真正优势不在于表达式树(操作起来很复杂),而在于我可以轻松混合和匹配各种功能.对于C ++样式的迭代器,是否存在.Where
,.Select
和.SelectMany
,.Skip
和.Take
和.Concat
的等效项?
The real strength of linq as a linq-consumer (i.e. to me) lies not in expression trees (which are complex to manipulate), but the ease with which I can mix and match various functions. Do the equivalents of .Where
, .Select
and .SelectMany
, .Skip
and .Take
and .Concat
exist for C++-style iterators?
这些对于我编写的各种通用代码非常方便.
These would be extremely handy for all sorts of common code I write.
我不在乎LINQ特定的问题,这里的关键问题是能够在更高层次上表达算法,而不是让C ++代码看起来像C#3.0.我希望能够表达结果是由每个序列的前n个元素串联而成的",然后在需要新序列的地方重用这样的表达式,而无需手动(贪婪地)实例化中间体.>
I don't care about LINQ-specifics, the key issue here is to be able to express algorithms at a higher level, not for C++ code to look like C# 3.0. I'd like to be able to express "the result is formed by the concatenation first n elements of each sequence" and then reuse such an expression wherever a new sequence is required - without needed to manually (and greedily) instantiate intermediates.
推荐答案
I don't have concrete experience with LINQ, but the Boost.Iterator library seems to approach what you're referring to.
这个想法是要有一个函数(在LINQ中,IIUC,它们采用扩展方法的形式,但这不是基本的),并带有一个迭代器和一个函数,将它们组合起来以创建一个新的迭代器.
The idea is to have functions (IIUC, in LINQ, they take the form of extension methods, but that's not fundamental), taking an iterator and a function, combining them to create a new iterator.
LINQ哪里"映射到make_filter_iterator
:
LINQ "Where" maps to make_filter_iterator
:
std::vector<int> vec = ...;
// An iterator skipping values less than "2":
boost::make_filter_iterator(_1 > 2, vec.begin())
LINQ选择"映射到make_transform_iterator
:
LINQ "Select" maps to make_transform_iterator
:
using namespace boost::lambda;
//An iterator over strings of length corresponding to the value
//of each element in "vec"
//For example, 2 yields "**", 3 "***" and so on.
boost::make_transform_iterator(construct<std::string>('*', _1), vec.begin())
它们可以组成:
//An iterator over strings of length corresponding to the value of each element
// in "vec", excluding those less than 2
std::vector<int> vec = ...;
boost::make_transform_iterator(construct<std::string>('*', _1),
boost::make_filter_iterator(_1 > 2, vec.begin())
)
但是,这有一些令人讨厌的事情:
However, there are a few annoying things with this:
-
make_xxx_iterator(some_functor, some_other_iterator)
返回的类型为xxx_iterator<type_of_some_functor, type_of_some_iterator>
- 使用boost :: bind,lambda或phoenix创建的函子的类型很快变得难以管理且繁琐.
- The type returned by
make_xxx_iterator(some_functor, some_other_iterator)
isxxx_iterator<type_of_some_functor, type_of_some_iterator>
- The type of a functor created using boost::bind, lambda, or phoenix quickly becomes unmanageably large and cumbersome to write.
这就是为什么我在上面的代码中避免将make_xxx_iterator
的结果分配给变量的原因. C ++ 0x的自动"功能将非常受欢迎.
That's why I avoided in the code above to assign the result of make_xxx_iterator
to a variable. C++0x "auto" feature will be pretty welcome there.
但是,C ++迭代器仍然不能单独存在":它们必须成对使用才有用.因此,即使使用自动"功能,也还是需要一口气:
But still, a C++ iterator can't live "alone": they have to come in pairs to be useful. So, even with "auto", it's still a mouthful:
auto begin = make_transform_iterator(construct<std::string>('*', _1),
make_filter_iterator(_1 > 2, vec.begin())
);
auto end = make_transform_iterator(construct<std::string>('*', _1),
make_filter_iterator(_1 > 2, vec.end())
);
避免使用lambda会使事情变得冗长但可管理:
Avoiding the use of lambda makes things verbose, but manageable:
struct MakeStringOf{
MakeStringOf(char C) : m_C(C){}
char m_C;
std::string operator()(int i){return std::string(m_C, i);}
};
struct IsGreaterThan{
IsGreaterThan(int I) : m_I(I){}
int m_I;
bool operator()(int i){return i > m_I;}
};
typedef boost::filter_iterator<
IsGreaterThan,
std::vector<int>::iterator
> filtered;
typedef boost::transform_iterator<
MakeStringOf,
filtered
> filtered_and_transformed;
filtered_and_transformed begin(
MakeStringOf('*'),
filtered(IsGreaterThan(2), vec.begin())
);
filtered_and_transformed end(
MakeStringOf('*'),
filtered(IsGreaterThan(2), vec.end())
);
(尚未)Boost.RangeEx库在这方面很有前途,因为它允许将两个迭代器合并在一个范围内.像这样:
The (not-yet)Boost.RangeEx library is promising in this respect, in that it allows to combine the two iterators in a single range. Something like:
auto filtered_and_transformed = make_transform_range(
make_filter_range(vec, _1 > 2),
construct<std::string>('*', _1)
);
这篇关于类似于C ++ LINQ的迭代器操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!