2

我正在使用控制台计算器上的 Visual c++,我正在创建一种让用户定义自定义线性函数的方法。这就是我难过的地方:一旦我获得了用户想要的函数名称、斜率和 y 截距,我需要使用这些数据来创建一个可以传递给 muParser 的可调用函数。

在 muParser 中,您可以像这样定义自定义函数:

double func(double x)
{
    return 5*x + 7; // return m*x + b;
}

MyParser.DefineFun("f", func);
MyParser.SetExpr("f(9.5) - pi");
double dResult = MyParser.Eval();

如何根据用户输入的值“m”和“b”动态创建这样的函数并将其传递给“DefineFun()”方法?这是我到目前为止所拥有的:

void cb_SetFunc(void)
{
    string FuncName, sM, sB;
    double dM, dB;
    bool GettingName = true;
    bool GettingM = true;
    bool GettingB = true;
    regex NumPattern("[+-]?(?:0|[1-9]\\d*)(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?");

    EchoLn(">>> First, enter the functions name. (Enter 'cancel' to abort)");
    EchoLn(">>> Only letters, numbers, and underscores can be used.");

    try
    {
        do // Get the function name
        {
            Echo(">>> Enter name: ");
            FuncName = GetLn();
            if (UserCanceled(FuncName)) return;

            if (!ValidVarName(FuncName))
            {
                EchoLn(">>> Please only use letters, numbers, and underscores.");
                continue;
            }
            GettingName = false;

        } while (GettingName);

        do // Get the function slope
        {
            Echo(">>> Enter slope (m): ");
            sM = GetLn();
            if (UserCanceled(sM)) return;

            if (!regex_match(sM, NumPattern))
            {
                EchoLn(">>> Please enter any constant number.");
                continue;
            }
            dM = atof(sM.c_str());
            GettingM = false;

        } while (GettingM);

        do // Get the function y-intercept
        {
            Echo(">>> Enter y-intercept (b): ");
            sB = GetLn();
            if (UserCanceled(sB)) return;

            if (!regex_match(sB, NumPattern))
            {
                EchoLn(">>> Please enter any constant number.");
                continue;
            }
            dB = atof(sB.c_str());
            GettingB = false;

        } while (GettingB);

            // ------------
            // TODO: Create function from dM (slope) and
            // dB (y-intercept) and pass to 'DefineFun()'
            // ------------
    }
    catch (...)
    {
        ErrMsg("An unexpected error occured while trying to set the function.");
    }
}

我在想没有办法为每个用户定义的函数定义一个单独的方法。我是否需要vector<pair<double, double>> FuncArgs;跟踪适当的斜率和 y 截距,然后从函数中动态调用它们?当我将它传递给时,我将如何指定要使用哪对DefineFun(FuncStrName, FuncMethod)

4

5 回答 5

4

您需要的(除了脚本语言解释器)称为“蹦床”。没有创建这些的标准解决方案,特别是因为它涉及在运行时创建代码。

当然,如果您接受固定数量的蹦床,您可以在编译时创建它们。如果它们都是线性的,这可能会更容易:

const int N = 20; // Arbitrary
int m[N] = { 0 };
int b[N] = { 0 };
template<int I> double f(double x) { return m[I] * x + b; }

这定义了一组 20 个分别f<0>...f<19>使用的函数m[0]...m[19]

编辑:

// Helper class template to instantiate all trampoline functions.
double (*fptr_array[N])(double) = { 0 };
template<int I> struct init_fptr<int I> {
  static const double (*fptr)(double) = fptr_array[I] = &f<I>;
  typedef init_fptr<I-1> recurse;
};
template<> struct init_fptr<-1> { };
于 2012-09-13T11:25:46.270 回答
1

我会保持简单:

#include <functional>

std::function<double(double)> f;   // this is your dynamic function

int slope, yintercept;             // populate from user input

f = [=](double x) -> double { return slope * x + yintercept; };

现在您可以将对象传递f给您的解析器,然后它可以f(x)自行调用。slope函数对象将和的捕获值打包yintercept

于 2012-09-13T10:51:52.187 回答
1

GiNaC是可以解析和评估数学表达式的 C++ 库。

于 2012-09-13T13:05:30.983 回答
0

尝试在您的应用程序中嵌入一些脚本语言。几年前,我将 Tcl 用于类似目的 - 但我不知道当前时间的最佳选择是什么。

您可以从 Tcl 开始,也可以自己搜索更好的东西:

请参阅:将 Tcl/Tk 添加到 C 应用程序

于 2012-09-13T10:43:22.937 回答
0

生成可绑定到 boost 函数的固定函数数组。

其他人已经说过类似的方法,但是由于我花时间编写代码,所以无论如何都在这里。

#include <boost/function.hpp>

enum {
    MAX_FUNC_SLOTS = 255
};

struct FuncSlot
{
    double (*f_)(double);
    boost::function<double(double)> closure_;
};

FuncSlot s_func_slots_[MAX_FUNC_SLOTS];

template <int Slot>
struct FuncSlotFunc
{
    static void init() {
        FuncSlotFunc<Slot-1>::init();
        s_func_slots_[Slot - 1].f_ = &FuncSlotFunc<Slot>::call;
    }
    static double call(double v) {
        return s_func_slots_[Slot - 1].closure_(v);
    }
};
template <> struct FuncSlotFunc<0> {
    static void init() {}
};

struct LinearTransform
{
    double m_;
    double c_;
    LinearTransform(double m, double c)
        : m_(m)
        , c_(c)
    {}
    double operator()(double v) const {
        return (v * m_) + c_;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    FuncSlotFunc<MAX_FUNC_SLOTS>::init();

    s_func_slots_[0].closure_ = LinearTransform(1, 0);
    s_func_slots_[1].closure_ = LinearTransform(5, 1);

    std::cout << s_func_slots_[0].f_(1.0) << std::endl; // should print 1
    std::cout << s_func_slots_[1].f_(1.0) << std::endl; // should print 6

    system("pause");
    return 0;
}

因此,您可以使用以下命令获取函数指针: s_func_slots_[xxx].f_ 并使用 s_func_slots_[xxx].closure_ 设置您的操作

于 2012-09-13T12:49:27.010 回答