What's the best way to define a piecewise function in C++, needed for example when working with splines?
Example:
f1(x) if x from [0, 5)
f(x) = f2(x) if x from [5, 10)
f3(x) if x from [10, 20)
My current approach looks like this:
class Function
{
virtual double operator()( double x ) = 0;
}
class SomeFun : public Function
{
// implements operator() in a meaningful way
}
class PiecewiseFunction : public Function
{
// holds functions along with the upper bound of the interval
// for which they are defined
// e.g. (5, f1), (10, f2), (20, f3)
std::map< double, Function* > fns;
virtual double operator()( double x )
{
// search for the first upper interval boundary which is greater than x
auto it = fns.lower_bound( x );
// ... and evaluate the underlying function.
return *(it->second)(x);
}
}
This approach lacks of checking if x is in the overall bounds of the function, like [0, 20) in the example above, I know, and perhaps the naming is not the best (Function vs. std::function and so on).
Any ideas how to do that in a smarter way? The approach uses the property of the keys to be sorted in a std::map. It's not about efficiency, it's more about a clean design.
SLICING
Not exactly part of the question, but in one of the comments, slicing is mentioned, here you can read about it.
std::map unable to handle polymorphism?
I corrected this in my code above.
An issue with the current design it doesn't allow for functions that are most naturally thought of as being undefined over certain intervals or points (like 0), but there are plenty of these, so it's another motivation for range-checking. Also Function needs to be replaced with Function* which necessitates some other changes in syntax.
class PiecewiseFunction : public Function
{
//Holds function and interval
std::map< std::pair<double,double>, Function* > fns;
double operator()( double x )
{
auto iter = std::find_if(fns.cbegin(), fns.cend(),
[=](const std::pair< std::pair<double,double>, Function*>& fn)
{
return x>=fn.first.first && x<fn.first.second;
});
if (iter == fns.end()) throw... //Or something
return (*iter->second)(x);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With