-1

Taking inspiration from: Defining a piecewise function (e.g. polynomial)

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>

struct Point {
  double x;
  double y;
};

class LinearFunction  {
public:
  // Pre-calculate slope `m` and intercept `c`
  LinearFunction(const Point &A, const Point &B){
    double den = A.x-B.x;
    m = (A.y-B.y)/den;
    c = (A.x*B.y-A.y*B.x)/den;
  }

  // Evaluate at x
  double operator()(double x){
    return m*x+c;
  }

private:
  double m = 0;
  double c = 0;
};

class PiecewiseFunction {
public:

  // Initialize (m and c) for all segments of the piecewise 
  // function as a map of <lb,ub> -> function pointer
  explicit PiecewiseFunction(std::vector<Point> &points){
    for (int i = 0; i < points.size()-1; i++){
      auto f = LinearFunction(points[i], points[i+1]);
      fns.insert(std::make_pair(std::make_pair(points[i].x, points[i+1].x), &f));
    }
  }


  double operator()( double x ) {
    // Match segment lb <= x < ub and evaluate
    auto iter = std::find_if(fns.cbegin(), fns.cend(),
            [=](const auto &fn) {return x>=fn.first.first && x<fn.first.second;});
    if (iter == fns.end()){
      return 0;
    } else {
      return iter->second->operator()(x);
    }
  }
private:
  std::map< std::pair<double,double>, LinearFunction*> fns;
};

int main() {
  std::vector<Point> points {{0,0},{0.5,1},{1,0}};
  PiecewiseFunction f{points};
  std::cout << "x = 0.5; f(x) = " << f(0.5) << std::endl;
  return 0;
}

which produces undefined behaviour:

x = 0.5; f(x) = 1.04297e-309

as the auto f = LinearFunction(points[i], points[i+1]); created in the loop goes out of scope.

I have been trying to find a solution that would let me create a piecewise function with e.g. 20 points

TobiMcNamobi
  • 4,687
  • 3
  • 33
  • 52
Oleg
  • 10,406
  • 3
  • 29
  • 57
  • What is `fns`? Where's the [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve)? And do you have do store a pointer in the pair? Can't you store an `LinearFunction` object instead? – Some programmer dude Jan 30 '18 at 11:46
  • 2
    Why not copy into the map instead? Your auto goes out of scope outside the loop. – Erik Alapää Jan 30 '18 at 11:46
  • I am amazed how much hate I get and not sure (@Someprogrammerdude) what's not Minimal, not Complete nor not verifiable about the example. I admit I got caught in the suggestions to store pointers rather than the objects themselves, which is a "Doh!" mistake, yet, people go chill. I put effort in the example and do not see why it can't help others that make the same, yet trivial mistake. – Oleg Jan 30 '18 at 12:03
  • IMHO a good MCVE can be copied anywhere where a compiler lives and can be compiled just so. And I don't see hate here. A lot of question marks, yes. But that's not hate. – TobiMcNamobi Jan 30 '18 at 12:26
  • @TobiMcNamobi I disagree on the hate, will amend the example to make it single-copy pasteable. – Oleg Jan 30 '18 at 12:41
  • 1
    are the some comments that have been deleted? Otherwise I am a bit puzzled about "how much hate" you interpret into those comments that are still here. I suggest you to calm down a bit, because what you see here is just the usual asking for clarification in comments by users that are just trying to help. Btw if you feel offended by any of the comments you may report it and let the moderators decide – 463035818_is_not_an_ai Jan 30 '18 at 13:07
  • @tobi303 Comments were edited, but the fact that I got a flash downvote and close votes just shows how dismissive this community is. Besides the trivial error, it took me time to read docs, try several things, before coming here (as last resort) and ask a well formed question (none of the questions I read are immediately copy-pasteable). If people are unwilling to post a trivial answer, just leave the question as is. The down and close votes make this question look ill-posed and useless, both points to which I disagree. – Oleg Jan 30 '18 at 13:28

1 Answers1

0

As suggested in the comments, I should store a copy not a dangling reference (as I am initializing in a loop).

The line in the inner loop goes from:

fns.insert(std::make_pair(std::make_pair(points[i].x, points[i+1].x), &f)); to

fns.insert(std::make_pair(std::make_pair(points[i].x, points[i+1].x), f));

and the variable declaration of fns, from:

std::map< std::pair<double,double>, LinearFunction *> fns; to:

std::map< std::pair<double,double>, LinearFunction> fns;

Finally,

return iter->second->operator()(x); to

return iter->second(x);

Oleg
  • 10,406
  • 3
  • 29
  • 57
  • 1
    You should also change condition in for loop `int i = 0; i < points.size(); i++`, now you are accessing element of vector out of range - for last iteration you read `points[i+1]`. – rafix07 Jan 30 '18 at 12:17
  • Thanks, I'll change in the question. – Oleg Jan 30 '18 at 12:18