1

次のような C の関数ツリーを作成したい: http://scr.hu/5rq/vdja0

したがって、基本的には結果を次のようにしたい: http://scr.hu/5rq/f04uu

wherexは私が提供できる変数です (float)。F0 から F6 は、2 つの引数を取るランダム関数です (乗算、加算、乱数の付与などの関数)。だから私の正確な質問は次のとおりです。どうすればそれを行うことができますか? 各関数によって与えられた正確な値を配列に格納することで、簡単に実行できることを私は知っています。しかし、異なる「x」値を取得することになると、複雑になります。私の最初の考えは、ツリーの各ノードにランダムな関数を追加する関数を作成することでしたが、そのツリーを作成する構造をどのように行うべきかわかりません。

typedef struct drzewo typ;

struct drzewo {
    typ *right;
    typ *left;
    typ *up;
    float *value; //what to do with this ?
};

「float *value;」という行をどうにか変更したいと思います。のような関数を格納できるFunction1(left->value,right->value);が実行されず、指定された引数を持つ関数の正確な値ではなく、Function1()またはFunction2()両方の引数を除算または乗算する関数などを意味するものに変換します。

いいえ、学校向けではありません。はい、これは遺伝的プログラミングを使用しようとする私の惨めな試みです。

4

2 に答える 2

5

関数を指す型を作成するには、関数プロトタイプを使用し、関数名を に置き換えます(*<typename>)。したがって、foo次のような関数がある場合:

float foo( int arg1, char *arg2 );

以下を使用して、foo のような関数へのポインターである型「foo_fn」を作成できます。

typedef float (*foo_fn)( int, char * );

次に、次のように構造に保存します。

typedef struct drzewo typ;
struct drzewo {
    typ *right;
    typ *left;
    typ *up;
    foo_fn fn_ptr;
};

さまざまなタイプの関数を保持する構造体が必要な場合 (戻り値の型が異なるか、パラメーターが異なる可能性があります)、それらをとして格納し、void *呼び出すときに適切な関数ポインター型にキャストすることができます。

于 2012-07-16T19:28:49.083 に答える
0

次のようなものから始めることができます。

typedef enum Operation
{
    VALUE,                                      // 0 argument (uses value)
    NOT, SIN, COS, TAN                          // 1 argument
    ADD, SUB, DIV, MUL, POW, LOG, AND, OR, XOR, // 2 argument
    SENTINEL;                                   // the last element
}
Operation;

typedef struct ExprNode
{
    Operation op;
    struct ExprNode * left;
    struct ExprNode * right;
    float value;
}
ExprNode;

typedef float (* EvalFunc)(ExprNode *);

EvalFunc eval_array[SENTINEL]; // array size is number of operations in enum

次に、それをある種の汎用ラッパー関数に入れ、そのラッパーを特別な目的の EvalFunc 関数から呼び出すことができます。これはすぐに記述できます。

float EVAL(ExprNode * node)
{
    return eval_array[node->op](node);
}

// for bonus points, use a #define
// #define EVAL(node) eval_array[node->op](node)

そこから、EvalFunc に一致する署名を持つ一連の関数が必要になります。たとえば、これは最も単純で、VALUE 指定子での使用に適しています。

float Eval_Value(ExprNode * node)
{
    return node->value;
}

または、これはもう少し詳細な(しかしまだ単純な)もので、追加を行います:

float Eval_Add(ExprNode * node)
{
    return EVAL(node->left) + EVAL(node->right);
}

これらの関数をすべてeval_array配列に入れることができるようになったので、次のようにインデックス付きルックアップですばやく呼び出すことができます。

eval_array[VALUE] = &Eval_Value;
eval_array[ADD]   = &Eval_Add;
// etc

ExprNodes に何らかの初期化関数を含めて、それらの初期化の退屈さを解消したい場合があります。

ExprNode_init(ExprNode * node, Operation _op, float _value, ExprNode * _left, ExprNode * _right)
{
    node->op    = _op;
    node->left  = _left;
    node->right = _right;
    node->value = _value;
}

次に、すべてのプロトタイプをヘッダー ファイルにコピーし、必要な場所に #include します。一連の式を作成し、次の方法で評価できます。

ExprNode n1, n2, n3, X, Y, Z;
ExprNode_init(&n1, SUM,   0, &n2,  &n3 ); // expression nodes: value is ignored
ExprNode_init(&n2, MUL,   0, &X,   &Y  ); // some functions only require 1 arg
ExprNode_init(&n3, DIV,   0, &Z,   &Y  ); // right can be omitted for these

ExprNode_init(&X,  VALUE, 3, NULL, NULL); // value nodes: value is not ignored
ExprNode_init(&Y,  VALUE, 4, NULL, NULL); // left and right are both ignored
ExprNode_init(&Z,  VALUE, 6, NULL, NULL); // both can be null

float result = EVAL(n1); // X*Y + Z/Y : X=3, Y=4, Z=6; should be 13.5
于 2012-07-16T20:14:58.137 に答える