【问题标题】:Composing adaptors in Boost::range在 Boost::range 中组合适配器
【发布时间】:2012-10-25 20:34:42
【问题描述】:

我开始使用 Boost::Range 以获得pipeline of lazy transforms in C++。我现在的问题是如何将管道分成更小的部分。假设我有:

int main(){
  auto map = boost::adaptors::transformed; // shorten the name
  auto sink = generate(1) | map([](int x){ return 2*x; })
                          | map([](int x){ return x+1; })
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}

我想用magic_transform替换前两张地图,即:

int main(){
  auto map = boost::adaptors::transformed; // shorten the name
  auto sink = generate(1) | magic_transform()
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}

如何写magic_transform?我查了Boost::Range's documentation,但我不能很好地掌握它。

附录:我正在寻找这样的类:

class magic_transform {
    ... run_pipeline(... input) {
        return input | map([](int x){ return 2*x; })
                     | map([](int x){ return x+1; });
};

【问题讨论】:

  • generate的返回类型是什么?我担心您的 sink 包含对在分号处到期的临时文件的引用。
  • generate的返回类型为boost::iterator_range,详情请查看liveworkspace.org/code/841508d3b54bed4181d4e9fb6058200f
  • 魔幻变换没有得到输入参数。你把它写成一个空函数。
  • 好吧,它更像是一个类而不是一个函数。见上文。
  • 你的需求还很模糊。是你想要的管道语法吗?围绕链式 lamdas 的变换提供了 | 的功能和外部使用。你真的想要|在你的 magic_transform 中,如果是,为什么?

标签: c++ boost functional-programming boost-range


【解决方案1】:

最困难的问题是找出代码中的返回类型。 decltype 和 lambdas 不能很好地混合在一起(see here),所以我们不得不想一个替代方法:

auto map = boost::adaptors::transformed;

namespace magic_transform
{
   std::function<int(int)> f1 = [](int x){ return 2*x; };
   std::function<int(int)> f2 = [](int x){ return x+1; };
   template <typename Range>
   auto run_pipeline(Range input) -> decltype(input | map(f1) | map(f1))
   {
        return input | map(f1) | map(f2);
   }
}

...
auto sink = magic_transform::run_pipeline(generate(1))
                          | map([](int x){ return 3*x; });

简单的解决方案是将 lambdas 粘贴到 std::function 中,这样我们就可以使用 decltype 来推断返回类型。我在示例中使用了命名空间magic_transform,但如果您愿意,也可以将此代码改编成一个类。 Here is a link 使您的代码适应上述内容。

另外,在这里使用std::function 可能有点矫枉过正。相反,您可以只声明两个普通函数 (example)。

我也在试验boost::any_range,似乎与 C+11 lambda 有一些不兼容,等等。我能得到的最接近的是以下 (example):

auto map = boost::adaptors::transformed;
using range = boost::any_range<
               const int,
               boost::forward_traversal_tag,
               const int&,
               std::ptrdiff_t
               >;

namespace magic_transform
{
    template <typename Range>
    range run_pipeline(Range r)
    {
        return r | map(std::function<int(int)>([](int x){ return 2*x; }))
             | map(std::function<int(int)>([](int x){ return x+1; }));
    }
}

int main(){
  auto sink = magic_transform::run_pipeline(boost::irange(0, 10))
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}

【讨论】:

  • 在问这里之前,我尝试了decltype,发现它对于这个应用程序来说太冗长了(你最终写了两次管道)。 any_range,OTOH,只允许你指定输入和输出,对吗?
  • 现在我明白了,any_range 指定了输出(值和参考)。有趣。
  • 另外,参数也可以使用any_range。例如:range run_pipeline(range r) 定义了一个作用于整数范围并返回整数范围的管道。
  • @brunonery:是的,我无法让any_range 与您的generate(1) 示例一起使用。
  • @brunonery:我发现你需要将迭代器标签更改为std::forward_iterator_taghere is a working example
【解决方案2】:

我认为可行:

auto magic_transform()->decltype(boost::adaptors::transformed(std::function<int(int)>())
{
    std::function<int(int)> retval = [](int x){ return [](int x){ return x+1; }(2*x);
    return boost::adaptors::transformed(retval);
}

但它可能不是您想要的。 :) (上面代码中的笑话:2*x+1 的链式 lambda,基本上实现上使用 decltype 来查找返回类型),

查看http://www.boost.org/doc/libs/1_46_1/boost/range/adaptor/transformed.hpp的源码,magic_transform要返回的类型是boost::range_detail::transform_holder&lt;T&gt;,其中T是函数的类型。

当您使用 lambda 在堆栈上执行此操作时,T 最终会成为一些非常窄的类型。如果您想在不暴露所有细节的情况下传递抽象转换,使用std::function&lt;outtype(intype)&gt; 可能是合理的(运行时开销会很小)。

希望有效。

【讨论】:

  • 是的,链接 lambdas 不是我想要的 :),请检查我的更新。我实际上可以使用autodecltype,但我想要更通用的东西。
  • 所以您想要某种方式将转换(按顺序组合)在一起并让结果成为转换?
  • 不仅转换,而且最终过滤器和其他范围适配器。
  • 范围适配器的结果类型已记录在案。例如,transformed 返回 boost::transformed_rangeboost.org/doc/libs/1_52_0/libs/range/doc/html/range/reference/…
猜你喜欢
  • 2015-12-29
  • 2012-08-06
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 2014-05-24
  • 1970-01-01
  • 1970-01-01
  • 2015-05-08
相关资源
最近更新 更多