Nhork
Nhork

Reputation: 21

functional approach to cpp

I am trying to build some nested functions like so (reduced example):

// the actual functions come from particle physics and are extremely ugly 
double f(double x){
  return 2*x+1;
}
//in reality a numerical derivative term
double F(const std::function<double(double)>& f, double x){
  return f(x) - f(x+1);
}
// in reality another higher order numerical derivative
double G(const std::function<double(double)>& f, double x){
  return f(x) + f(x+1);
}
// in reality a function where the index is supposed to control the degree of derivatives of the function
double H(const std::function<double(double)>& f, double x, int switch){
  if(0 == switch){ 
    return G(f(x)); 
  } else {
    return F(f(x));
  }
}

this is the goal (2d for demonstration):

double sum=0;
for(int i=0; i<1;++i){
  for(int j=0; j<1;++j){
    sum += H(H(f,i),j); 
  }
}

So two things:

  1. I want to use a function H to switch between F and G (my actual problem has 3 cases).
  2. I would like to find a way/concept of calling H(H(...)...) the problem with that is the type of the function f which is the first argument of H, when I pass it a second time the type is no longer double(double) but double(std::function<double(double)>,double(double),double) or so...

Or in other terms: https://i.sstatic.net/XkbmJ.gif (cannot post images, not enough rep)

Upvotes: 2

Views: 132

Answers (2)

lisyarus
lisyarus

Reputation: 15587

Firstly, don't explicitly use std::function, but make your function argument templated instead, like

template <typename Arg>
double F(Arg const & f, double x){
  return f(x) - f(x+1);
}

This enables a lot more room to both optimization and genericity.

Secondly, turn all your functions into returning a lambda:

template <typename Arg>
auto F(Arg const & f){
  return [f](double x){ return f(x) - f(x+1); };
}

template <typename Arg>
auto G(Arg const & f){
  return [f](double x){ return f(x) + f(x+1); };
}

template <typename Arg>
std::function<double(double)> H(Arg const & f, int which){
  if(0 == which){ 
    return F(f);
  } else {
    return G(f);
  }
}

Note the auto return type: this is needed for returning lambdas to work.

Now you don't call F(f,x), but rather F transforms f into it's derivative (or something else), which can be now applied to an x. These can now be used like

F(f)(0.1); // apply F(f) to x=0.1

F(F(G(f)))(0.5); // apply F(F(G(f))) to x=0.5

H(H(f,0),1)(4.2); // apply H(H(f,0),1) to x=4.2

Upvotes: 2

Caleth
Caleth

Reputation: 63162

If you have a value for the inner f and switch (N.B. change that name, switch is a flow control construct), then you can partially apply H and then pass that to H

std::function<double(double)> partial_H(std::function<double(double)> f, int choice)
{
    return [f, choice](double x) { return H(f, x, choice); };
}

Upvotes: 1

Related Questions