gpt4 book ai didi

c++ - C++ 中数据聚合和后处理的习惯用法

转载 作者:行者123 更新时间:2023-12-03 06:53:42 28 4
gpt4 key购买 nike

编程中的一项常见任务是动态处理数据,并在收集完所有数据后进行一些后期处理。一个简单的例子就是计算平均值(和其他统计数据),您可以在其中创建这样的类

class Statistic {
public:
Statistic() : nr(0), sum(0.0), avg(0.0) {}

void add(double x) { sum += x; ++nr; }
void process() { avg = sum / nr; }

private:
int nr;
double sum;
double avg;
};

这种方法的一个缺点是,我们总是必须记住在添加所有数据后调用 process() 函数。由于在 C++ 中我们有类似 RAII 的东西,这似乎不是一个理想的解决方案。

以Ruby为例,我们可以这样写代码

class Avg
attr_reader :avg

def initialize
@nr = 0
@sum = 0.0
@avg = nil
if block_given?
yield self
process
end
end

def add(x)
@nr += 1
@sum += x.to_f
end

def process
@avg = @sum / @nr
end
end

然后我们可以这样调用

avg = Avg.new do |a|
data.each {|x| a.add(x)}
end

并且 process 方法在退出 block 时自动调用。

C++ 中是否有可以提供类似功能的习语?

澄清一下:这个问题不是关于计算平均值的。它是关于以下模式:将数据提供给对象,然后,当所有数据都提供时,触发处理步骤。我对基于上下文的方法自动触发处理步骤很感兴趣——或者这在 C++ 中不是一个好主意的原因。

最佳答案

“惯用平均数”

我不懂 Ruby,但你不能直接翻译成语。我知道计算平均值只是一个示例,所以让我们看看我们可以从该示例中得到什么...

计算总和和容器中元素平均值的惯用方法是 std::accumulate:

std::vector<double> data;
// ... fill data ...
auto sum = std::accumulate( a.begin(), a.end() , 0.0);
auto avg = sum / a.size();

构建 block 是容器、迭代器和算法。

如果容器中没有现成可用的要处理的元素,您仍然可以使用相同的算法,因为算法只关心迭代器。编写自己的迭代器需要一些样板文件。以下只是一个玩具示例,计算调用同一函数一定次数的结果的平均值:

#include <numeric>

template <typename F>
struct my_iter {
F f;
size_t count;
my_iter(size_t count, F f) : count(count),f(f) {}
my_iter& operator++() {
--count;
return *this;
}
auto operator*() { return f(); }
bool operator==(const my_iter& other) const { return count == other.count;}
};


int main()
{
auto f = [](){return 1.;};
auto begin = my_iter{5,f};
auto end = my_iter{0,f};
auto sum = std::accumulate( begin, end, 0.0);
auto avg = sum / 5;
std::cout << sum << " " << avg;
}

输出是:

5 1

假设您有一个要调用的函数的参数 vector ,那么调用 std::accumulate 是直截了当的:

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

int main()
{
auto f = [](int x){return x;};
std::vector<int> v = {1,2,5,10};

auto sum = std::accumulate( v.begin(), v.end(), 0.0, [f](int accu,int add) {
return accu + f(add);
});
auto avg = sum / 5;
std::cout << sum << " " << avg;
}

std::accumulate 的最后一个参数指定元素如何相加。我没有直接将它们相加,而是将调用函数的结果相加。输出是:

18 3.6

针对您的实际问题

从字面上看您的问题并回答 RAII 部分,这是您可以在统计类中使用 RAII 的一种方法:

struct StatisticCollector {
private:
Statistic& s;
public:
StatisticCollector(Statistic& s) : s(s) {}
~StatisticCollector() { s.process(); }
};

int main()
{
Statistic stat;
{
StatisticCollector sc{stat};
//for (...)
// stat.add( x );
} // <- destructor is called here

}

PS: 最后但并非最不重要的一点是,还有一种方法可以保持简单。您的类定义有点破损,因为所有结果都是私有(private)的。一旦你解决了这个问题,很明显你不需要 RAII 来确保 process 被调用:

class Statistic {
public:
Statistic() : nr(0), sum(0.0), avg(0.0) {}

void add(double x) { sum += x; ++nr; }
double process() { return sum / nr; }
private:
int nr;
double sum;
};

在我看来,这是正确的界面。用户不能忘记调用 process 因为要获得结果,他们需要调用它。如果该类的唯一目的是累积数字和处理结果,则不应封装结果。结果供类的用户存储。

关于c++ - C++ 中数据聚合和后处理的习惯用法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64050045/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com