- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
编程中的一项常见任务是动态处理数据,并在收集完所有数据后进行一些后期处理。一个简单的例子就是计算平均值(和其他统计数据),您可以在其中创建这样的类
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/
我编写的代码主要供个人使用,但我正在考虑发布我最初开发供个人使用的应用程序(科学模拟/可视化)。 我的一个习惯是在类中使用一个main方法来单独测试类的运行情况。我认为这在某种程度上可能是不好的(毫无
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: How do I convince programmers in my team to do TDD? 在从
假设我想测试是否有一个名为“Z”的驱动器。第一步是这样的; Get-PSProvider | Select-Object -Property Drives 这个给我; Drives: ... {C,
这是对 an old answer to a question about the necessity of functools.partial 的一种跟进: 虽然这个答案非常清楚地解释了这种现象及其
Perl 习惯很难改掉。两种语言之间的变量声明、作用域、全局/局部是不同的。是否有一组推荐的 python 语言习语可以使从 perl 编码到 python 编码的过渡不那么痛苦。 细微的变量拼写错误
我是一名优秀的程序员,十分优秀!