C ++类似LINQ的迭代器操作

时间:2009-10-14 10:16:14

标签: c++ linq functional-programming iterator

受到Linq的污染,我不愿意放弃它。但是,对于某些事情,我只需要使用C ++。

linq作为linq-consumer(即对我来说)的真正优势不在于表达树(操作起来很复杂),而是我可以轻松地混合和匹配各种功能。对于C ++样式的迭代器,是否存在.Where.Select.SelectMany.Skip以及.Take.Concat的等价物?

对于我编写的各种常用代码,这些都非常方便。

我不关心LINQ特有的,这里的关键问题是能够在更高级别表达算法,而不是C ++代码看起来像C#3.0。我希望能够表达“结果是由每个序列的前n个元素串联形成的”,然后在需要新序列的地方重用这样的表达式 - 无需手动(并贪婪地)实例化中间体。

8 个答案:

答案 0 :(得分:8)

我正在研究(C#LINQ)类C ++头文件库。

这是:http://code.google.com/p/boolinq/

我想得到任何反馈......

更新:

以下是boolinq 2.0的新链接:https://github.com/k06a/boolinq

所有源代码都基于单个头文件 - https://github.com/k06a/boolinq/blob/master/boolinq/boolinq.h

超短:大约60种不同的操作少于800行!

答案 1 :(得分:6)

我想推荐P-Stade.Oven库供您参考。这是一个强大的增强库,用于STL范围,具有许多类似LINQ的功能,包括.Where,.Select .Skip .Take和.Concat等效。

答案 2 :(得分:4)

请参阅this Google网上论坛帖子。

vector<int> numbers = {1, 2, 3, 4, 8, 5, 9 , 24, 19, 15, 12 } 
auto query = 
    from(numbers).
        where([](int i) { return i < 15 && i > 10}). 
        select(fields::full_object); 

我找不到更多或更少“官方”或被广泛接受的内容,但您可以尝试联系原始帖子的作者。

答案 3 :(得分:4)

我没有LINQ的具体经验,但Boost.Iterator库似乎接近你所指的。

我们的想法是拥有函数(IIUC,在LINQ中,它们采用扩展方法的形式,但这不是基础),使用迭代器和函数,将它们组合起来创建一个新的迭代器。

LINQ“Where”映射到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

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())
)

然而,这有一些烦人的事情:

  • make_xxx_iterator(some_functor, some_other_iterator)返回的类型为xxx_iterator<type_of_some_functor, type_of_some_iterator>
  • 使用boost :: bind,lambda或phoenix创建的仿函数的类型很快就变得难以管理,并且编写起来很麻烦。

这就是我在上面的代码中避免将make_xxx_iterator的结果分配给变量的原因。 C ++ 0x“自动”功能将非常受欢迎。

但是,C ++迭代器仍然无法“独自”生活:它们必须成对出现才能发挥作用。所以,即使是“自动”,它仍然是满口的:

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会使事情变得冗长,但却易于管理:

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库在这方面很有前途,因为它允许在单个范围内组合两个迭代器。类似的东西:

auto filtered_and_transformed = make_transform_range(
    make_filter_range(vec, _1 > 2),
    construct<std::string>('*', _1)
);

答案 4 :(得分:3)

在C ++ 11中使用Boost.RangeLinq,您可以用非常类似的方式编写Linq查询:

std::vector<int> numbers = { 1, 2, 3, 4 };
auto r = LINQ(from(x, numbers) where(x > 2) select(x * x));
for (auto x : r) printf("%i\n", x);

将输出:

9
16

答案 5 :(得分:2)

这是另一个alternative,它只是boost和stl算法的包装器,因此您可以获得这些实现的所有性能优势。

它的工作原理如下:

std::vector<int> xs;
auto count = from(xs)
   .select([](int x){return x*x;})
   .where([](int x){return x > 16;})
   .count();
auto xs2 = from(xs)
   .select([](int x){return x*x;})
   .to<std::vector<int>>();

请注意,某些方法会返回空范围的代理,例如

std::vector<int> xs;
auto max = from(xs)
   .select([](int x){return x*x;})
   .where([](int x){return x > 16;})
   .max()
   .value_or(0);

答案 6 :(得分:0)

我个人不时使用cpplinq。很好无论如何,它都不会试图成为 LINQ 的完美翻译,并且会保持足够的C ++身份(如果可以的话),使其本身具有很强的实力。另外,除了标准的 C ++ 11 之外,您没有任何依赖关系。只要您能忍受,最好选择 cpplinq

答案 7 :(得分:0)

Abseil库有很多https://github.com/abseil/abseil-cpp/blob/master/absl/algorithm/container.h,还有很多容器函数:c_all_ofc_any_ofc_none_ofc_findc_count,{{ 1}},c_count_ifc_replace_copyc_unique_copyc_min_elementc_max_element