0

while working with the library Armadillo I figured out that there is a recurrent pattern when dealing with missing values. If, for example I want to perform the var operation on rows of a matrix and handle missing values I will proceed like this

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)));
        }
    }
}

Now I realised that it is often the case to perform the same procedure but with different functions (e.g. mean, median, sum etc.). I was then wondering how I can write a generic version of this function so that it can accept different functions as parameters. My attempt for now is:

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)));
        }
    }
} 

but when I execute

 FuncOnMat(A,r,mean);

it does not work. I have the feeling I need to feed a functor or a lambda. Which is the best way to proceed?

RDGuida
  • 546
  • 4
  • 15
  • When you say it does not work, what error do you get specifically? – Cameron Dec 03 '15 at 16:27
  • `error: no matching function for call to 'FuncOnMat(arma::mat&, arma::vec&, )'` – RDGuida Dec 03 '15 at 16:28
  • 3
    Well there you go then; your function is fine (though I would use `Func const&` just in case you ever do pass in a lambda or functor), it's just that `mean` can refer to more than one overload so the compiler has no idea which one you're trying to pass. You'll [need a cast](http://stackoverflow.com/q/2942426/21475) to tell it which one you want (or use a lambda just for the nicer syntax). – Cameron Dec 03 '15 at 16:31

2 Answers2

0

I think you should use a function pointer as third argument and not a template in this situation, you may need to use a template only if your matrix class can be of different types, templates usually are made to build multiple functions that perform the same operation on different types, they work at COMPILE time, not a runtime, so a functor/lambda/function pointer cannot be a template argument.

Here is a solution with a function pointer, that can be used also with a lambda.

Since in your example there is no clue of the type used inside M and v I'm using "float" as base type of M and v, but if you are using double simply replace it...

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 example:

FuncOnMat(m, v, [](float a) -> float { return a / 0.2; });
gabry
  • 1,370
  • 11
  • 26
0

I think your ideas are correct, but unlike with gabry's function pointer version, a template version needs FuncOnMat's definition to be visible everywhere it is used. So you would probably put it in a header file.

I threw together a quick template-based example, with everything crammed into the same file to keep it simple. I used std::vector for mocked-up mat and vec classes:

#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);
}
Christopher Oicles
  • 3,017
  • 16
  • 11