4

考虑以下最小示例:

#include <range/v3/all.hpp>
#include <iostream>

namespace rng = ranges::v3;

int main() 
{
    std::vector<int> v { 6, 2, 3, 4, 5, 6 };
    auto f    = [](auto a, auto b) { return a*0.3 + b*0.7;};
    auto rng  = v | rng::view::partial_sum(f);

    for(auto i : rng)
    {
        std::cout<<i<<" ";
    }
}

这输出

6 3 2 3 4 5 

我本来希望在这里看到双数,但结果显然是整数。这与 的行为相反view::transform

这样做的原因是因为在实现中,running-sum 值具有对应于源范围的类型:

semiregular_t<range_value_type_t<Rng>> sum_;

这是故意的还是错误的?


讨论:我看到在尝试获取有效返回类型时遇到了麻烦,因为转换函数同时使用源范围和结果范围作为参数并产生返回类型。下一个应用程序使用 source-range-type 和这个返回类型来产生另一个(可能不同的)返回类型,依此类推。

这样,原则上,将源值类型与转换函数的结果类型重复链接。仅当结果类型“收敛”到所有其他中间结果都可以转换为的特定类型时,这种重复迭代才会产生可用的东西(在上面的示例中,这种类型是double,在第一次调用转换函数之后已经获得)。

有了这个观察,人们可以提出一种解决方法:应用二进制变换函数给定的次数,并使用common_type结果范围的 as 值类型(如果发现收敛,过早停止)。在最简单的情况下,迭代次数只有一次。如果这个迭代没有导致一些合理的事情,人们仍然可以求助于源值类型(或编译器错误)。

为了清楚起见,这里是上面示例的应用程序:

First iteration : f(int,int)    -> yields "double"
Second iteration: f(int,double) -> yields "double"
Third iteration : f(int,double) -> yields "double"

在第三次迭代之后,模式收敛,所以停止并选择 common-typedouble作为返回范围的 value_type。

我不确定这种方法在所有理论情况下是否完全有效,但至少它在第一个示例中给出了两倍——我想这是每个人都强烈期待的。

4

1 回答 1

5

ranges::view::partial_sum通过设计反映std::partial_sum. 如果你运行:

#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>

int main() 
{
    std::vector<int> v { 6, 2, 3, 4, 5, 6 };
    auto f = [](auto a, auto b) { return a*0.3 + b*0.7; };
    std::vector<double> rng;
    std::partial_sum(v.begin(), v.end(), std::back_inserter(rng), f);

    for(auto i : rng)
    {
        std::cout<<i<<" ";
    }
}

您应该得到与 OP 中的程序完全相同的输出。与许多 range-v3 视图一样,该视图的工作是计算与标准算法计算的结果序列相同的结果序列,但这样做是懒惰的。

std::partial_sum被指定对一个类型与输入范围的值类型相同的累加器进行操作。[partial.sum]/2说:

效果:对于非空范围,该函数创建一个acc类型为InputIterator的值类型的累加器,用 初始化它*first,并将结果分配给*resulti对于按[first + 1, last)顺序排列的每个迭代器,acc然后由acc = acc + *ior修改,acc = binary_­op(acc, *i)并将结果分配给*(result + (i - first))

为了等效地表现,ranges::view::partial_sum还使用类型是输入范围的值类型的累加器。

在 OP 的情况下,您可以通过使用double作为输入范围的类型来获得所需的结果。使用 range-v3,这很容易通过组合来即时完成ranges::view::transform(ranges::convert_to<double>{})

#include <range/v3/all.hpp>
#include <iostream>

namespace rng = ranges::v3;

int main() 
{
    std::vector<int> v { 6, 2, 3, 4, 5, 6 };
    auto f    = [](auto a, auto b) { return a*0.3 + b*0.7;};
    auto rng  = v | rng::view::transform(rng::convert_to<double>{}) |
        rng::view::partial_sum(f);

    for(auto i : rng)
    {
        std::cout<<i<<" ";
    }
}

产生所需的输出

6 3.2 3.06 3.718 4.6154 5.58462
于 2017-09-10T16:05:05.953 回答