【问题标题】:Accepting and executing different functions in a function as a parameter在一个函数中接受和执行不同的函数作为参数
【发布时间】:2016-03-08 08:52:15
【问题描述】:

在使用 Armadillo 库时,我发现在处理缺失值时存在一种循环模式。例如,如果我想对矩阵的行执行var 操作并处理缺失值,我将像这样进行

void varianceRows(const mat& M,vec& v)
{
    for(uint i=0;i<M.n_rows;i++)     // variance calculated on rows
    {
        if(M.row(i).is_finite())
            v(i) = var(M.row(i));
        else if(!any(M.row(i)>0))
            v(i) = NAN;
        else
        {
            vec b=M.row(i);
            v(i) = var(b.elem(find_finite(b)));
        }
    }
}

现在我意识到执行相同程序但功能不同(例如meanmediansum 等)通常是这种情况。然后我想知道如何编写这个函数的通用版本,以便它可以接受不同的函数作为参数。我现在的尝试是:

template<typename Func>
void FuncOnMat(const mat& M,vec& v,Func func)
{
    for(uint i=0;i<M.n_rows;i++)     // operation calculated on rows
    {
        if(M.row(i).is_finite())
            v(i) = func(M.row(i));
        else if(!any(M.row(i)>0))
            v(i) = NAN;
        else
        {
            vec b=M.row(i);
            v(i) = func(b.elem(find_finite(b)));
        }
    }
} 

但是当我执行时

 FuncOnMat(A,r,mean);

它不起作用。我觉得我需要提供一个仿函数或lambda。最好的方法是什么?

【问题讨论】:

  • 当你说它不起作用时,你具体得到什么错误?
  • error: no matching function for call to 'FuncOnMat(arma::mat&amp;, arma::vec&amp;, &lt;unresolved overloaded function type&gt;)'
  • 那么你去吧;你的函数很好(虽然我会使用 Func const&amp; 以防你传入 lambda 或仿函数),只是 mean 可以引用多个重载,所以编译器不知道你是哪一个试图通过。您将 need a cast 告诉它您想要哪个(或使用 lambda 来获得更好的语法)。

标签: c++ templates c++11 lambda function-pointers


【解决方案1】:

我认为在这种情况下你应该使用函数指针作为第三个参数而不是模板,只有当你的矩阵类可以是不同类型时,你才可能需要使用模板,模板通常用于构建执行的多个函数不同类型上的相同操作,它们在编译时工作,而不是运行时,因此仿函数/lambda/函数指针不能是模板参数。

这是一个带有函数指针的解决方案,它也可以与 lambda 一起使用。

由于在您的示例中没有关于 M 和 v 中使用的类型的线索,我使用“float”作为 M 和 v 的基本类型,但如果您使用的是 double,只需替换它...

void FuncOnMat(const mat& M,vec& v,float (*func)(float))
{
  for(uint i=0;i<M.n_rows;i++)     // operation calculated on rows
  {
      if(M.row(i).is_finite())
        v(i) = func(M.row(i));
      else if(!any(M.row(i)>0))
          v(i) = NAN;
      else
      {
          vec b=M.row(i);
          v(i) = func(b.elem(find_finite(b)));
      }
  }
} 

Lambda 示例:

FuncOnMat(m, v, [](float a) -> float { return a / 0.2; });

【讨论】:

  • 是的,输入参数通常是vec类型,但逻辑是一样的:)
【解决方案2】:

我认为您的想法是正确的,但与 gabry 的函数指针版本不同,模板版本需要 FuncOnMat 的定义在任何使用它的地方都可见。所以你可能会把它放在一个头文件中。

我整理了一个基于模板的快速示例,将所有内容都塞进同一个文件中以保持简单。我使用 std::vector 模拟 mat 和 vec 类:

#include <vector>
#include <iostream>

using vec = std::vector<double>;
using mat = std::vector<vec>;

template <typename Func>
vec FuncOnMat(const mat& m, Func func) {
    vec v;
    for(const auto& row : m) {
        v.push_back(func(row));
    }
    return v;
}

// Mean is a functor which calculates a vec's elements' arithmetic mean
struct Mean {
    double operator ()(const vec& v) const {
        double sum = 0;
        for(double x : v) {
            sum += x;
        }
        return sum / v.size();
    }
};

// dumpvec prints the elements of a vec
void dumpvec(const vec& v) {
    for(auto x : v) {
        std::cout << x << "  ";
    }
    std::cout << "\n";
}

int main() {
    // define a matrix object
    mat m {{1, 2, 3}, {-3, 2, -1}, {0, 0, 0}};

    // use a functor object
    Mean mean_functor;
    vec  mean_vec = FuncOnMat(m, mean_functor);

    // use a lambda
    vec max_vec = FuncOnMat(m, [](const vec& v) {
        auto maxx = v.front();
        for(auto x : v) { if(x > maxx) { maxx = x; }} // yes, vec[0] checked redundantly
        return maxx;
    });

    // dump results
    std::cout << "Mean Functor: ";
    dumpvec(mean_vec);

    std::cout << "Max Lambda:   ";
    dumpvec(max_vec);
}

【讨论】:

    猜你喜欢
    • 2017-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2022-01-09
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多