次のようなものから始めることができます。
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