【问题标题】:C++ LINQ-like iterator operations类似 C++ LINQ 的迭代器操作
【发布时间】:2009-10-14 10:16:14
【问题描述】:

被Linq污染了,舍不得放弃。但是,对于某些事情,我只需要使用 C++。

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

这些对于我编写的各种常见代码来说都非常方便。

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

【问题讨论】:

  • 可能重复:stackoverflow.com/questions/232222/… -- 那么它们可能又有所不同我怀疑这是否真的是一个“精确”重复
  • 我对 LINQ 不感兴趣,但对 C++ 中的函数式列表处理感兴趣。
  • 具体来说,lambda 很好,但并不重要。

标签: c++ linq functional-programming iterator


【解决方案1】:

我正在开发(C# LINQ)-like C++ header-only library。

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

我想得到任何反馈...

更新:

这里是 boolinq 2.0 的新链接:https://github.com/k06a/boolinq

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

它超级短:不到 800 行,大约 60 种不同的操作!

【讨论】:

  • 该库看起来非常漂亮,它是我见过的最接近按预期工作的库。但是它不是在启用 c++11 的 gcc 4.7 上为我构建的
  • @lurscher,这个库是使用 Visual C++ 开发的。但我想让它尽可能地便携和跨平台。我试图支持 mingw 4.4,但它不支持 C++11 lambdas。我会尽快添加 gcc 支持。或者你可以帮助我)
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

    LINQ“位置”映射到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&lt;type_of_some_functor, type_of_some_iterator&gt;
    • 使用 boost::bind、lambda 或 phoenix 创建的仿函数类型很快变得难以管理,并且编写起来很麻烦。

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

    但是,C++ 迭代器仍然不能“单独”存在:它们必须成对出现才能有用。因此,即使使用“auto”,它仍然是一口:

    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】:

      请参阅 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); 
      

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

      【讨论】:

      • 那是使用支持 auto 和 lambda 的 c++0x 编译器,如果没有 lambda,它会变得有点麻烦 ([](int i){...})
      • 谢谢,太好了!但是,链接的线程并不容易使用,而且无论如何,它看起来有点过于专注于 LINQ - 我不太关心语法,我只想要 map/reduce/filter/concat 等......平原像 STL 这样的旧函数式积累就足够了——只要它在今天可用...
      【解决方案5】:

      使用 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
      

      【讨论】:

      • fromwhere 是宏吗?
      • 不,他们不是。唯一的宏是LINQ 宏。其余部分使用预处理器解析。
      • 来自code inspectionwhere 是一个operator() 重载,给定一个完美转发的Range&amp;&amp; rPredicate p。从那里移交给Boost.Range等。
      【解决方案6】:

      这是另一个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);
      

      【讨论】:

      • 干得好,但有几件事:void for_each(...){ return ...; } 听起来不对,每当你有auto x(...) -&gt; decltype(some long expression) 并且你需要在x 中重用该类型(比如selectreversetake 等),只需说 decltype(x(...)) 并传递所有参数,对于许多事情来说,会大大短一些。另外,你有to_vector,而不是to&lt;T&gt;。 :)
      • Xeo:感谢您的反馈。修复了tofor_each。虽然我不太明白如何获得您缩短decltypes 工作的建议。你能给我举个更具体的例子吗?
      • 事后看来,我想知道为什么你需要在正文中重用类型,因为linq_range 的构造函数不是explicit
      • Xeo:在某些时候它没有工作,而不是“重用”类型。但这似乎不再是问题。我正在改变它。
      • 我想知道将传递给from(...)的原始容器的引用存储在第一个linq_range中是否有任何好处,而不是先将其转换为iterator_range。需要一些额外的元模板编程才能使linq_rangerange 存储为引用或值,具体取决于具体情况。我最初的猜测是编译器优化不会有任何区别。
      【解决方案7】:

      就个人而言,我不时使用cpplinq。这是相当不错的。无论如何,它都不会尝试成为 LINQ 的完美翻译,并且如果您愿意的话,它会保持足够的 C++ 身份,这使得它本身非常强大。另外,除了 C++11 标准之外,您不采用任何依赖项。只要你能忍受,你就可以选择 cpplinq

      【讨论】:

        【解决方案8】:

        Abseil 库有很多 https://github.com/abseil/abseil-cpp/blob/master/absl/algorithm/container.h 有很多容器函数:c_all_ofc_any_ofc_none_ofc_findc_countc_count_ifc_replace_copy、@9876543029@、@9876 @、c_max_elementc_accumulate

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-10-22
          • 2020-11-21
          • 2011-01-31
          • 1970-01-01
          • 2021-04-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多