gpt4 book ai didi

c++ - 向数学解析器添加条件和函数

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:00:21 30 4
gpt4 key购买 nike

我有一个我构建的基于二叉树的数学表达式解析器,它非常适合“普通”数学,例如:(3.5 * 2) ^ 1 / (1 << 6) .然而,我想稍微扩展它以添加一个三元选择运算符,镜像来自 C: {expr} ? {true-expr} : {false-expr} 的那个。 .我还想添加功能,例如 sin(x)ave(...) .

然而,我不知道如何处理这个问题(由于评估的工作方式),我也无法在网络上找到任何内容,至少以非基于语法的方式(我想避免语法分析器生成器,如果可能的话)。

我的解析器当前通过评估中缀表达式并立即将其转换为树来工作,然后可以从那里评估树,即:它是标准表达式树。

目前我的评估器看起来像这样:

struct Node
{
int nType;
union
{
unsigned long dwOperator;
BOOL bValue;
int nValue; //for indices, args & functions
number_t fValue;
char* szValue; //for string literals to pass to functions
};

Node* pLeft;
Node* pRight;
};

number_t EvaluateTree(Node* pNode)
{
if(pNode == NULL)
return 0.0f;

int nType = pNode->nType;
if(nType == TOKEN_OPERATOR)
{
number_t fLeft = EvaluateTree(pNode->pLeft);
number_t fRight = EvaluateTree(pNode->pRight);
switch(pNode->dwOperator)
{
case '+': return fLeft + fRight;
case '-': return fLeft - fRight;
case '*': return fLeft * fRight;
case '/': return fLeft / fRight;
case '^': return pow(fLeft,fRight);
case '_': return pow(fLeft,1.0f/fRight);
case '%': return fmod(fLeft,fRight);

//case '?': return bSelect = ?;
//case ':': return (bSelect) ? fLeft : fRight;

//case '>': return fLeft > fRight;
//case '<': return fLeft < fRight;
//case '>=': return fLeft >= fRight;
//case '<=': return fLeft <= fRight;
//case '==': return fLeft == fRight;
//case '!=': return fLeft != fRight;
//case '||': return fLeft || fRight;
//case '&&': return fLeft && fRight;

case '&': return static_cast<number_t>(static_cast<unsigned long>(fLeft) & static_cast<unsigned long>(fRight));
case '|': return static_cast<number_t>(static_cast<unsigned long>(fLeft) | static_cast<unsigned long>(fRight));
case '~': return static_cast<number_t>(~static_cast<unsigned long>(fRight));
case '>>': return static_cast<number_t>(static_cast<unsigned long>(fLeft) >> static_cast<unsigned long>(fRight));
case '<<': return static_cast<number_t>(static_cast<unsigned long>(fLeft) << static_cast<unsigned long>(fRight));

default:
{
printf("ERROR: Invalid Operator Found\n");
return 0.0f;
}
}
}
else if(nType == TOKEN_NUMBER)
return pNode->fValue;
else if(nType == TOKEN_CALL)
return CreateCall(pNode); //not implemented
else if(nType == TOKEN_GLOBAL)
return GetGlobal(pNode);
else if(nType == TOKEN_ARGUMENT)
return GetArgument(pNode);
else if(nType == TOKEN_STRING)
return 0.0f;

return 0.0f;
}

关于如何完成此操作的任何提示/指针/建议或有用的链接?


一小部分示例(根据要求):

我已经在做的事情

输入:2 * (3 ^ 1.5) - 4 / (1 << 3)

输出:In-Order: 2.0 * 3.0 ^ 1.5 - 4.0 / 1.0 << 3.0

Pre-Order: - * 2.0 ^ 3.0 1.5 / 4.0 << 1.0 3.0

Post-Order: 2.0 3.0 1.5 ^ * 4.0 1.0 3.0 << / -

Result: 9.892304

我要补充的内容

输入:(GetDay() == 31) ? -15.5 : 8.4

输出:8.4

31日输出:-15.5

输入:max([0],20) (其中 [0] 表示参数 0,并且 [0] = 35)

输出:20

输入:(GetField('employees','years_of_service',[0]) >= 10) ? 0.15 : 0.07 (其中 [0] 是参数 0,并且 [0] 设置为有效索引)

输出(如果员工的 years_of_service 小于 10:0.15

否则输出:0.07

它基本上是数学加上一些受 C 启发的添加,除了参数不是按名称传递,而是按索引传递,并且字符串由单引号而不是双引号转义。

当我完成最后一点时,我希望编译字节码或 JIT 它,因为我计划将其用于游戏或数学相关程序等输入集数据不变的东西,但是输入集可以改变,但它被频繁使用,所以它需要“快速”,并且需要非程序员也可以使用。

最佳答案

正确的做法是? and : 取决于解析器生成的树。我会假装解析器生成一棵树

      ?
b :
t f

首先你不需要在切换之前评估树,并且大多数地方你改变了一些东西

fLeft + fRight;

进入

EvaluateTree(pNode->pLeft) + EvaluateTree(pNode->pRight);

用所有不同的运算符替换 +。

对于 ?: 你做....

case ':': return 0.0f; /* this is an error in the parse tree */
case '?': if (!(pNode && pNode->pLeft && pNode->pRight &&
pNode->pRight->pLeft && pNode->pRight->pRight))
/* another error in the parse tree */
return 0.0f;
return EvaluateBool(pNode->pLeft) ?
EvaluateTree(pNode->pRight->pLeft) :
EvaluateTree(pNode->pRight->pRight) ;

对于 EvaluateBool 的定义,您有几个选择。 C方式或多或少

BOOL EvaluateBool(Node* pNode)
{
return (EvaluateTree(pNode) == 0.0) ? FALSE : TRUE;
}

然后您需要定义“<”和返回 0.0 表示 false 的 friend ,以及其他任何表示 true 的定义。值 -1 是一个非常流行的真值,尽管通常用于将 bool 存储在 int 中。

更结构化的方法是将所有返回 bool 值的运算符(如“<”)移动到 EvaluateBool 的主体中,并使其或多或少像 EvaluateTree 一样工作。

最后,除了使三元运算符 ?: 使用两个节点,您还可以更改节点(和解析器)的定义,使其最多具有三个子树,那么大多数运算符将有两棵树,但是 ?:会有三个。也许像

case '?': return EvaluateBool(pNode->pLeft) ?
EvaluateTree(pNode->pMiddle) :
EvaluateTree(pNode->pRight) ;

但随后您将不得不重写您的前序、中序、后序树遍历。

第二部分,函数。一种方法是将函数名称存储在 szValue 中。另一个是根据功能为 nType 设置一堆不同的值。您将不得不在解析器中选择一些规则,并在解释器中使用它。你可以做类似的事情......

else if(nType == TOKEN_CALL)
return EvaluateFunc(pNode);

然后 EvaluateFunc 可能看起来像

number_t EvaluateFunc(Node* pNode)
{
if ((pNode == NULL) || (pNode->szValue == NULL))
return 0.0f;
if (0 == strcmp('cos', pNode->szValue))
return my_cos(EvaluateTree(pNode->pLeft));
else if (0 == strcmp('gcd', pNode->szValue))
return my_gcd(EvaluateTree(pNode->pLeft),
EvaluateTree(pNode->pRight));
/* etc */
else /* unknown function */ return 0.0f;
}

看起来是个有趣的项目,好好享受吧!

关于c++ - 向数学解析器添加条件和函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3262889/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com