现在的位置: 首页 > 综合 > 正文

一个支持函数,浮点数表达式求值算法

2018年03月30日 ⁄ 综合 ⁄ 共 1686字 ⁄ 字号 评论关闭

由于工作原因,需要写一个可以解析字符串表达式的接口,要求支持常用函数,函数多参数,函数嵌套,函数声明如下:

bool eval(const std::string& expr, double *result);

记得大学《数据结构》课程写过表达式求值算法,遂在其上修改。

经典的表达式求值算法基本思想是:先由中缀表达式转换为后缀表达式,然后对后缀表达式求值。但是该算法只支持个位数运算,且不支持对函数的处理,因此这也是本程序需要解决的俩个主要问题。

问题1. 支持浮点运算

这个比较好解决,只需要将原来存放操作数的char型变量改为double类型,存放后缀表达式的字符串改成链表,增加一个read_operand函数,用于从字符串中读取一个double类型的操作数。设计存放后缀表达式的结构体如下:

enum OPTOR_TYPE
{
	OT_OPERATOR, // 操作符
	OT_OPERAND,  // 操作数
};

/* optor类型,用于存放操作符,或操作数 */
struct optor
{
	union
	{
		basic_operator *operatorp; // 操作符类型,包括 运算符和函数
		double operand;  // 操作数,统一用double表示
	}val;
	OPTOR_TYPE type;
       // 省去其他函数
};

// 后缀表达式存放在list<optor*>类型的变量里面。

问题2. 支持函数

 针对这个问题,首先想到的是把所有函数替换成函数值,然后对没有函数的表达式求值,这就需要定义两个函数:

double eval_expr(string expr) , // 求包含函数的表达式的值

double eval_func(string expr),// 求函数的值,函数参数可能是一个包含函数的表达式

然后互相递归调用。由于这种逻辑比较复杂,故弃之不用。

换一种思维方式,前面说的方法是把函数当成操作数,用返回值替换函数,现在把函数当成操作符来处理看会怎样。每个操作符都有一个或多个操作数,函数也是如此,只是操作符和操作数的先后位置不一样,但操作数总是在操作符或函数相邻的左右,由于在中缀表达式转换成后缀表达式的过程中,操作数和操作符是单独处理的(操作数直接取出加到后缀表达式,而操作符需要一些入栈操作),所以操作数在操作符或函数的左还是右就显得不那么重要了。因此要处理函数,只需在中缀转后缀的过程中添加几条规则就可以了:

函数名:直接入栈
   (     :直接入栈
    ,    :操作符出栈,直到遇到(
    )    :操作符出栈,直到遇到(,弹出(,判断栈顶元素是否为函数名,是则出栈。

话不多说,先看一下测试用例:

#include <iostream>
#include <string>
#include "eval.h"

using namespace std;
// 测试用例, 用例不是很全,仅作示范
string cases[] = {"sin(3.14/2)+cos(3.14/2)+pow(2,3)+sqrt(3)+lg(100)+ln(7)", // 测试所有支持的函数
                  "12.4+2*13-53.345/3+2^3+7%4",                             // 测试基本运算符
                  "2*sin(3.14/lg(100))+pow(pow(2,1+2),2) + lg(100)",        // 测试函数嵌套、多参数函数
                  "2.3+5*4--6",                                             // 测试错误表达式
                  "2+3*sin1(3.14/2)"};                                      // 测试不支持函数

int main()
{
	double result;
	int ncase = sizeof(cases)/sizeof(cases[0]);
	for(int i = 0; i < ncase; ++i)
	{
		cout << cases[i] << " = ";
		if(eval(cases[i], &result))
			cout << result << endl;           // 输出表达式值 
		else
			cout << "wrong expression" << endl;  // 错误的表达式 
	}
	return 0;
}

以下是程序输出:

用计算器算了以下,值都正确,但有误差,这是浮点计算的特点。

跨平台的源码下载地址(可直接编译成静态库):https://github.com/fdxuwei/eval

抱歉!评论已关闭.