假设我想编写一个函数,比如说,在某个范围内返回x的f(x)之和。
double func() {
double sum = 0.;
for (int i=0; i<100; i++) {
sum += f(i);
}
return sum;
}
但有时,除了最后的总和,我还需要部分术语,所以我可以做到
pair<vector<double>,double> func_terms() {
double sum = 0.;
vector<double> terms(100);
for (int i=0; i<100; i++) {
terms[i] = f(i);
sum += terms[i];
}
return {terms, sum};
}
问题是,这是代码重复。在这个例子中,这看起来非常无害,但是假设函数更大(这种情况促使我问这个),并且两个版本的区别在于少数几行(在这个例子中,逻辑是同样只有后一个版本在添加到sum之前将该术语存储在向量中,并返回一个带有该向量的对;任何其他逻辑都是等价的)。然后我将不得不编写和维护两个几乎完全相同的函数版本,只有几行和return语句不同。我的问题是,是否有成语/模式/最佳实践来处理这类问题。可以让我分享两个版本之间的公共代码。
简而言之:我可以编写两个函数,并且必须维护两个几乎完全相同的版本。或者我可以使用后者但是每当我需要总和时这将是非常浪费的,这是不可接受的。处理这个问题的最佳模式是什么?
我认为使用C ++ 17可以做类似
的事情template<bool partials>
double func(vector<double>* terms=nullptr) {
double sum = 0.;
if constexpr (partials)
*terms = vector<double>(100);
for (int i=0; i<100; i++) {
if constexpr (partials) {
(*terms)[i] = f(i);
sum += (*terms)[i];
} else {
sum += f(i);
}
}
return sum;
}
除了使用指针之外,它与我的意图非常接近(我不能使用引用,因为terms
可能是空的)。
答案 0 :(得分:2)
你的问题标题是“编写一个可能返回一个或多个值的函数”,但它不止于此;正如您的示例所示,该函数在返回结果之前很久也会执行许多不同的操作。这个广泛的问题确实没有通用的解决方案。
但是,对于具体案例,您已经解释过我想提供一种低技术解决方案。您可以根据第三个函数简单地实现这两个函数,并为第三个函数提供一个参数来确定是否执行了额外的功能。
这是一个C ++ 17示例,其中第三个函数被称为func_impl
,并且或多或少隐藏在命名空间内,以使func
和{{1}的客户端更轻松}:
func_terms
是否太低技术取决于您并且取决于您的确切问题。
答案 1 :(得分:1)
这是一个解决方案,建议将一个可选指针传递给vector,并仅在存在时填充它。我将其删除,因为其他答案也提到了它,后一种解决方案看起来更优雅。
您可以将计算抽象为迭代器,因此调用者仍然非常简单,并且不会复制任何代码:
auto make_transform_counting_iterator(int i) {
return boost::make_transform_iterator(
boost::make_counting_iterator(i),
f);
}
auto my_begin() {
return make_transform_counting_iterator(0);
}
auto my_end() {
return make_transform_counting_iterator(100);
}
double only_sum() {
return std::accumulate(my_begin(), my_end(), 0.0);
}
std::vector<double> fill_terms() {
std::vector<double> result;
std::copy(my_begin(), my_end(), std::back_inserter(result));
return result;
}
答案 2 :(得分:0)
一种简单的方法是编写一个通用函数并使用输入参数来做条件。像这样:
double logic(vector<double>* terms) {
double sum = 0.;
for (int i=0; i<100; i++) {
if (terms != NULL) {
terms.push_back(i);
}
sum += terms[i];
}
return sum;
}
double func() {
return logic(NULL);
}
pair<vector<double>,double> func_terms() {
vector<double> terms;
double sum = logic(&ret);
return {terms, sum};
}
此方法用于许多条件。逻辑可能非常复杂并且有许多输入选项。您可以通过不同的参数使用相同的逻辑。 但在大多数情况下,我们不需要那么多的返回值,只需要不同的输入参数。
答案 3 :(得分:0)
如果你不喜欢:
std::pair<std::vector<double>, double> func_terms() {
std::vector<double> terms(100);
for (int i = 0; i != 100; ++i) {
terms[i] = f(i);
}
return {terms, std::accumulate(terms.begin(), terms.end(), 0.)};
}
然后可能:
template <typename Accumulator>
Accumulator& func_helper(Accumulator& acc) {
for (int i=0; i<100; i++) {
acc(f(i));
}
return acc;
}
double func()
{
double sum = 0;
func_helper([&sum](double d) { sum += d; });
return sum;
}
std::pair<std::vector<double>, double> func_terms() {
double sum = 0.;
std::vector<double> terms;
func_helper([&](double d) {
sum += d;
terms.push_back(d);
});
return {terms, sum};
}
答案 4 :(得分:0)
我制定了一个OOP解决方案,其中基类总是计算总和并使当前术语可用于派生类,这样:
class Func
{
public:
Func() { sum = 0.; }
void func()
{
for (int i=0; i<100; i++)
{
double term = f(i);
sum += term;
useCurrentTerm(term);
}
}
double getSum() const { return sum; }
protected:
virtual void useCurrentTerm(double) {} //do nothing
private:
double f(double d){ return d * 42;}
double sum;
};
因此派生类可以实现虚方法并设置额外的属性(除了sum):
class FuncWithTerms : public Func
{
public:
FuncWithTerms() { terms.reserve(100); }
std::vector<double> getTerms() const { return terms; }
protected:
void useCurrentTerm(double t) { terms.push_back(t); }
private:
std::vector<double> terms;
};
如果一个人不想公开这些类,可以回退到函数并将它们用作façade(但两个函数,但现在非常易于管理):
double sum_only_func()
{
Func f;
f.func();
return f.getSum();
}
std::pair<std::vector<double>, double> with_terms_func()
{
FuncWithTerms fwt;
fwt.func();
return { fwt.getTerms(), fwt.getSum() };
}
答案 5 :(得分:0)
对于这种情况,最简单的解决方案我认为是这样的:
double f(int x) { return x * x; }
auto terms(int count) {
auto res = vector<double>{};
generate_n(back_inserter(res), count, [i=0]() mutable {return f(i++);});
return res;
}
auto func_terms(int count) {
const auto ts = terms(count);
return make_pair(ts, accumulate(begin(ts), end(ts), 0.0));
}
auto func(int count) {
return func_terms(count).second;
}
但是这种方法为原始版本提供了func()
不同的性能特征。目前的STL有很多方法,但这突出了STL不适用于可组合性的区域。 Ranges v3库提供了一种更好的方法来为这类问题编写算法,并且正在为将来的C ++版本进行标准化。
通常,可组合性/重用与最佳性能之间存在权衡。最好的C ++让我们吃蛋糕并吃掉它但是这是一个例子,正在进行工作以提供标准的C ++更好的方法来处理这种情况。