免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: fly3ds
打印 上一主题 下一主题

[算法] 怎么解析一个数学表达式 [复制链接]

论坛徽章:
1
综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00
21 [报告]
发表于 2013-10-19 08:34 |只看该作者
回复 20# cokeboL

* / + -的都加进去了 现在要搞搞优先级

   

论坛徽章:
1
综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00
22 [报告]
发表于 2013-10-20 11:41 |只看该作者
回复 21# fly3ds


已经可以识别并计算浮点数!  

-bash-4.1$ ./expre
12 + 3 - 4 / 5 * 2.1 + 5
12 + 3 - 4 / 5 * 2.1 + 5

12 + 3 - 4 / 10.500000 + 5

12 + 3 - 0.380952 + 5

15 - 0.380952 + 5

15 - 5.380952
9.619048
-bash-4.1$

论坛徽章:
4
白羊座
日期:2013-09-17 21:59:30技术图书徽章
日期:2013-10-12 22:16:03白羊座
日期:2013-10-14 11:01:40双子座
日期:2013-12-17 18:26:39
23 [报告]
发表于 2013-10-20 11:52 |只看该作者
回复 8# w_anthony
我好像记得是后缀表达式,也就是逆波兰表示法。碰到操作数的时候压入堆栈,碰到操作符的时候处理。

   

论坛徽章:
9
摩羯座
日期:2013-08-15 15:18:48狮子座
日期:2013-09-12 18:07:47金牛座
日期:2013-09-16 13:23:09辰龙
日期:2013-10-09 09:03:27白羊座
日期:2013-10-17 13:32:44子鼠
日期:2014-04-23 15:09:38戌狗
日期:2014-09-17 11:37:542015年亚洲杯之韩国
日期:2015-03-26 10:16:442015亚冠之武里南联
日期:2015-08-18 14:55:52
24 [报告]
发表于 2013-10-21 09:16 |只看该作者
回复 23# 井蛙夏虫


    你说的方法听起来挺靠谱的,我其实也记不清到底是哪种了,毕竟大学课本上的东西。
虽然前缀和后缀都可以解决这个问题,但是大学的时候应该不会深入讲递归,我想应该是你说的这个方法。

论坛徽章:
14
水瓶座
日期:2014-06-10 09:51:0215-16赛季CBA联赛之江苏
日期:2017-11-27 11:42:3515-16赛季CBA联赛之八一
日期:2017-04-12 14:26:2815-16赛季CBA联赛之吉林
日期:2016-08-20 10:43:1215-16赛季CBA联赛之广夏
日期:2016-06-23 09:53:58程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-09 06:20:0015-16赛季CBA联赛之上海
日期:2015-12-25 16:40:3515-16赛季CBA联赛之广夏
日期:2015-12-22 09:39:36程序设计版块每日发帖之星
日期:2015-08-24 06:20:002015亚冠之德黑兰石油
日期:2015-08-07 09:57:302015年辞旧岁徽章
日期:2015-03-03 16:54:15
25 [报告]
发表于 2013-10-21 09:53 |只看该作者
直接参考Louden的《编译原理与实践》
第四章有个现成的caculator,基本可以解决你的问题
用的是递归下降,就是不能处理浮点数

论坛徽章:
4
白羊座
日期:2013-09-17 21:59:30技术图书徽章
日期:2013-10-12 22:16:03白羊座
日期:2013-10-14 11:01:40双子座
日期:2013-12-17 18:26:39
26 [报告]
发表于 2013-10-22 10:20 |只看该作者
本帖最后由 井蛙夏虫 于 2013-10-22 10:21 编辑

回复 24# w_anthony
我也是在大学时学数据结构看到的这种方法。编译原理时就是说LL这些方法了。
如果会lex和yacc的话就简单的多,只需要写词法规则和语法规则,不需要自己解析了。


   

论坛徽章:
9
摩羯座
日期:2013-08-15 15:18:48狮子座
日期:2013-09-12 18:07:47金牛座
日期:2013-09-16 13:23:09辰龙
日期:2013-10-09 09:03:27白羊座
日期:2013-10-17 13:32:44子鼠
日期:2014-04-23 15:09:38戌狗
日期:2014-09-17 11:37:542015年亚洲杯之韩国
日期:2015-03-26 10:16:442015亚冠之武里南联
日期:2015-08-18 14:55:52
27 [报告]
发表于 2013-10-22 15:05 |只看该作者
本帖最后由 w_anthony 于 2013-10-23 10:54 编辑

今天上班没什么事,无聊写了个,测试运行了下,效果不错。
方法是解析生成一颗与运算优先级相关的树,然后前缀递归计算结果。

代码BUG了,先编辑下。
现在修改过了,应该没问题了……

再优化一下,去掉了while,因为这个while的次数是可预见的。
  1. #include <stdio.h>

  2. enum ElemType
  3. {
  4.         Type_Number = 0,
  5.         Type_Root,
  6.         Type_Plus,
  7.         Type_Subtract,
  8.         Type_Prior,
  9.         Type_Multiply,
  10.         Type_Divide,
  11. };

  12. class CCalcElem
  13. {
  14. public:
  15.         CCalcElem() : m_iType(Type_Number) {}
  16.         ~CCalcElem()
  17.         {
  18.                 if (m_iType != Type_Number)
  19.                 {
  20.                         delete m_lpLeft;
  21.                         delete m_lpRight;
  22.                 }
  23.         }
  24. protected:
  25.         CCalcElem(const CCalcElem&) {}
  26.         const CCalcElem& operator=(const CCalcElem&) { return *this; }
  27. public:
  28.         int m_iType;
  29.         union {
  30.                 double m_dlNumber;
  31.                 struct {
  32.                         CCalcElem* m_lpLeft;
  33.                         CCalcElem* m_lpRight;
  34.                 };
  35.         };
  36.         CCalcElem* m_lpParent;
  37. };

  38. static bool IsBlank(char ch)
  39. {
  40.         return ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'));
  41. }

  42. int AnalyseNumber(const char*& lpExpress, CCalcElem* lpTree);

  43. static int AnalyseOperator(const char*& lpExpress, CCalcElem* lpTree)
  44. {
  45.         while (IsBlank(*lpExpress))
  46.                 ++lpExpress;
  47.         int iType;
  48.         switch (*lpExpress)
  49.         {
  50.         case '+':
  51.                 iType = Type_Plus;
  52.                 break;
  53.         case '-':
  54.                 iType = Type_Subtract;
  55.                 break;
  56.         case '*':
  57.                 iType = Type_Multiply;
  58.                 break;
  59.         case '/':
  60.                 iType = Type_Divide;
  61.                 break;
  62.         case ')':
  63.                 ++lpExpress;
  64.                 return 1;
  65.         case '\0':
  66.                 return 0;
  67.         default:
  68.                 return -1;
  69.         }
  70.         ++lpExpress;
  71.         CCalcElem* lpNode = lpTree->m_lpParent;
  72.         if (iType < Type_Prior)
  73.         {
  74.                 if (lpNode->m_iType != Type_Root)
  75.                 {
  76.                         lpNode = lpNode->m_lpParent;
  77.                         if (lpNode->m_iType != Type_Root)
  78.                                 lpNode = lpNode->m_lpParent;
  79.                 }
  80.         }
  81.         else
  82.         {
  83.                 if ((lpNode->m_iType != Type_Root) && (lpNode->m_iType > Type_Prior))
  84.                         lpNode = lpNode->m_lpParent;
  85.         }
  86.         CCalcElem* const lpElem = new CCalcElem;
  87.         lpElem->m_lpParent = lpNode;
  88.         CCalcElem* const lpNodeRight = lpNode->m_lpRight;
  89.         lpNode->m_lpRight = lpElem;
  90.         lpElem->m_iType = iType;
  91.         lpElem->m_lpLeft = lpNodeRight;
  92.         lpElem->m_lpRight = NULL;
  93.         return AnalyseNumber(lpExpress, lpElem);
  94. }

  95. int AnalyseNumber(const char*& lpExpress, CCalcElem* lpTree)
  96. {
  97.         while (IsBlank(*lpExpress))
  98.                 ++lpExpress;
  99.         if (*lpExpress == '(')
  100.         {
  101.                 ++lpExpress;
  102.                 const int iType = lpTree->m_iType;
  103.                 lpTree->m_iType = Type_Root;
  104.                 const int iResult = AnalyseNumber(lpExpress, lpTree);
  105.                 if (iResult > 0)
  106.                 {
  107.                         lpTree->m_iType = iType;
  108.                         return AnalyseOperator(lpExpress, lpTree->m_lpRight);
  109.                 }
  110.         }
  111.         else
  112.         {
  113.                 double dl;
  114.                 int count;
  115.                 if (sscanf(lpExpress, "%lf%n", &dl, &count) > 0)
  116.                 {
  117.                         lpTree->m_lpRight = new CCalcElem;
  118.                         lpTree->m_lpRight->m_lpParent = lpTree;
  119.                         lpTree->m_lpRight->m_dlNumber = dl;
  120.                         lpExpress += count;
  121.                         return AnalyseOperator(lpExpress, lpTree->m_lpRight);
  122.                 }
  123.         }
  124.         return -1;
  125. }

  126. static double CalcTree(CCalcElem* lpTree)
  127. {
  128.         if (lpTree->m_iType != Type_Number)
  129.         {
  130.                 const double dlLeft = CalcTree(lpTree->m_lpLeft);
  131.                 const double dlRight = CalcTree(lpTree->m_lpRight);
  132.                 const int iType = lpTree->m_iType;
  133.                 if (iType == Type_Plus)
  134.                         return (dlLeft + dlRight);
  135.                 else if (iType == Type_Subtract)
  136.                         return (dlLeft - dlRight);
  137.                 else if (iType == Type_Multiply)
  138.                         return (dlLeft * dlRight);
  139.                 else
  140.                         return (dlLeft / dlRight);
  141.         }
  142.         return lpTree->m_dlNumber;
  143. }

  144. static bool AnalyseExpress(const char* lpExpress, double& dlValue)
  145. {
  146.         CCalcElem nElem;
  147.         nElem.m_iType = Type_Root;
  148.         nElem.m_lpLeft = NULL;
  149.         nElem.m_lpRight = NULL;
  150.         if (AnalyseNumber(lpExpress, &nElem) == 0)
  151.         {
  152.                 dlValue = CalcTree(nElem.m_lpRight);
  153.                 return true;
  154.         }
  155.         return false;
  156. }

  157. int main(int, char**)
  158. {
  159.         printf("%s", "Input expression:");
  160.         char str[512];
  161.         gets(str);
  162.         double dlResult;
  163.         if (AnalyseExpress(str, dlResult))
  164.                 printf("%s = %lf\r\n", str, dlResult);
  165.         else
  166.                 printf("%s", "Invalid expression!\r\n");
  167.         return 0;
  168. }
复制代码

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
28 [报告]
发表于 2013-10-22 16:03 |只看该作者
本帖最后由 yulihua49 于 2013-10-22 16:15 编辑
fly3ds 发表于 2013-10-18 20:11
https://github.com/fly3ds/linux_test

有兴趣看看这里面的expre.c,还是没有完成,不过照这个思路编下去 ...

用stptok去分割操作符:
rp=stptok(arg->cp,p,
                        sizeof(arg->argbuf)-(p-arg->argbuf),",+*-/%()#;");
这是我的计算器里的代码,这里不处理=,它在外部处理。
要处理:#注释,;结束。
函数执行完,判断*rp是哪个操作符即可。

stptok()可以在网上搜到,有源码。

论坛徽章:
9
摩羯座
日期:2013-08-15 15:18:48狮子座
日期:2013-09-12 18:07:47金牛座
日期:2013-09-16 13:23:09辰龙
日期:2013-10-09 09:03:27白羊座
日期:2013-10-17 13:32:44子鼠
日期:2014-04-23 15:09:38戌狗
日期:2014-09-17 11:37:542015年亚洲杯之韩国
日期:2015-03-26 10:16:442015亚冠之武里南联
日期:2015-08-18 14:55:52
29 [报告]
发表于 2013-10-22 23:19 |只看该作者
fly3ds 发表于 2013-10-20 11:41
已经可以识别并计算浮点数!  

-bash-4.1$ ./expre
12 + 3 - 4 / 5 * 2.1 + 5
12 + 3 - 4 / 5 * 2.1 + 5

12 + 3 - 4 / 10.500000 + 5

12 + 3 - 0.380952 + 5

15 - 0.380952 + 5

15 - 5.380952
9.619048
-bash-4.1$

你不觉得第一步应该先算/,再算*么?

论坛徽章:
1
综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00
30 [报告]
发表于 2013-10-23 03:52 |只看该作者
回复 29# w_anthony

这是常见的处理方式,不过我看不是必需的。
   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP