2

私はコンソール電卓で作業しているビジュアル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; }

f<0>...f<19>これは、それぞれを使用する20 個の関数のセットを定義し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 から始めるか、より良いものを自分で検索できます。

参照: C アプリケーションへの Tcl/Tk の追加

于 2012-09-13T10:43:22.937 に答える
0

ブースト関数にバインド可能な関数の固定配列を生成します。

他の方が似たような方法を既に述べていましたが、コードを書くのに時間がかかったので、とにかくここにあります。

#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 に答える