在C中制作函数树

时间:2012-07-16 19:22:54

标签: c tree evolutionary-algorithm genetic

我想在C中创建一个函数树,它看起来像这样: http://scr.hu/5rq/vdja0

基本上我希望结果如下:http://scr.hu/5rq/f04uu

其中x是我可以提供的变量(浮点数)。 F0到F6是随机函数,它有两个参数(函数如乘法,加法或给出随机数)。 所以我的确切问题是:我怎么能这样做? 我知道可以通过存储数组中每个函数给出的精确值来轻松完成。 但是当谈到获得不同的“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()表示将两个参数分开或将它们相乘的函数或等等。

不,这不是为了学校,是的,这是我使用遗传编程的悲惨尝试。

2 个答案:

答案 0 :(得分:5)

要创建指向函数的类型,请使用函数原型并将函数名替换为(*<typename>)。因此,如果您的函数foo看起来像:

float foo( int arg1, char *arg2 );

您可以创建一个类型“foo_fn”,它是一个指向函数的指针,如foo:

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

然后将其存储在您的结构中:

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

如果你想拥有一个包含各种类型函数的结构(可能有不同的返回类型或采用不同的参数),你可能只想将它们存储为void *并将它们转换为正确的函数指针当你想打电话时输入。

答案 1 :(得分: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