Noel19283746
Noel19283746

Reputation: 17

Is there a way to use a mathematical expression as a function argument?

I am building a polynomial class and I would like to construct an object like this:

Variable x('x');

Polynomial p = 4(x^3) + 2(x^2) - 8;

I was wondering if there was a way to construct the Polynomial using a mathematical expression.

I have tried using a va_list and a "Term" class but it would be nicer if it could be done as shown above.

Upvotes: -1

Views: 123

Answers (2)

Marek R
Marek R

Reputation: 38209

You didn't stet your question clearly. I understood your question like this:

You wish to describe polynomial in code is such way that it resembles notation used in mathematics.

This can be done, by leveraging operator overloading and providing helper classes. This can be started like this:

class Term {
public:
    constexpr Term() noexcept = default;

    constexpr Term(double x) noexcept
        : m_order { 0 }
        , m_coefficient { x }
    {
    }

    constexpr Term(double x, size_t pow) noexcept
        : m_order { pow }
        , m_coefficient { x }
    {
    }

    constexpr double coeff() const noexcept
    {
        return m_coefficient;
    }

    constexpr size_t order() const noexcept
    {
        return m_order;
    }

    constexpr Term negative() const noexcept
    {
        return { -m_coefficient, m_order };
    }

private:
    size_t m_order = 0;
    double m_coefficient = 0.0;
};

class Polynomial {
public:
    Polynomial() = default;
    Polynomial(std::initializer_list<double> coeff)
        : a { std::rbegin(coeff), std::rend(coeff) }
    {
    }

    Polynomial(std::initializer_list<Term> terms)
        : a {}
    {
        auto max_order = std::max_element(std::rbegin(terms), std::rend(terms), [](auto a, auto b) {
            return a.order() < b.order();
        })->order();
        a.resize(max_order + 1, 0.0);
        for (auto term : terms) {
            a[term.order()] += term.coeff();
        }
    }

    Polynomial(Term term)
    {
        addTerm(term);
    }

    Polynomial& addTerm(Term term)
    {
        a.resize(std::max(a.size(), term.order() + 1), 0.0);
        a[term.order()] += term.coeff();
        return *this;
    }

    double operator()(double x) const noexcept
    {
        double r = 0;
        for (auto ai : a) {
            r *= x;
            r += ai;
        }
        return r;
    }

public:
    std::vector<double> a = { 0.0 };
};

constexpr Term operator^(Term term, size_t pow) noexcept
{
    return { term.coeff(), term.order() * pow };
}

constexpr Term operator*(double a, Term b) noexcept
{
    return { a * b.coeff(), b.order() };
}

Polynomial operator+(Term a, Term b)
{
    return { a, b };
}

Polynomial operator+(Polynomial&& poly, Term b)
{
    poly.addTerm(b);
    return { std::move(poly) };
}

Polynomial operator-(Polynomial&& poly, Term term)
{
    return std::move(poly) + term.negative();
}

constexpr auto x = Term { 1.0, 1 };

https://godbolt.org/z/8qWazo4MY

Upvotes: 1

Christophe
Christophe

Reputation: 73617

Yes, this is possible. But there is no language support for it. There is no equivalent in C++ for JavaScript or Python's eval(). So it won't be so easy.

One simple way to make this is to use the interpreter pattern for representing expressions. You'd then execute the expression using a "context" that stores a map for the variable values. More explanations here.

There are other approaches, but in the end, you'll have to parse the expression (if it comes as a string), create some kind of abstract syntax tree and either have a simple execution engine for the tree or some more elaborate symbolic math engine, i.e. on that could apply math rules (like distribution of factors, etc) on the expression tree.

Moreover, the classes that use such expressions as parameter must foresee the additional work by design. Typically, if using the interpreter pattern approach, your polynomial would probably be itself an interpreter with interpreter arguments.

Upvotes: 0

Related Questions