免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 7757 | 回复: 12
打印 上一主题 下一主题

[算法] 写了一个计算四则混合运算的小程序,大家来评审一下 ^_^ [复制链接]

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-03-16 22:24 |只看该作者 |倒序浏览
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
2 [报告]
发表于 2013-03-17 08:38 |只看该作者
我觉得你应该先把四则运算的BNF搞清楚,然后写递归向下就很简单了,比如这样:

simpleexpr ::= number | '(' expr ')'
expr ::= ['-'] simpleexpr { binop expr }

这样总共就四个函数了:read_number, simpleexpr, binop和expr,结构会很清晰。

另外最好定义一下比较清晰的函数签名。

以上是参考Lua脚本语言的parser部分提出的。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
3 [报告]
发表于 2013-03-17 10:08 |只看该作者
本帖最后由 starwing83 于 2013-03-17 10:48 编辑

这个是从Lua源代码里面单独抽出来的(思路是Lua的,代码是自己写的),只有你的一半不到,你可以看看(我没有处理空格):
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <setjmp.h>
  4. #include <math.h>

  5. typedef enum BinOpr {
  6.     OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW, OP_NON
  7. } BinOpr;

  8. static struct { int left, right; } binop_prio[] = {
  9.     {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, {10,9},
  10. };

  11. #define UNARY_PRIO 8

  12. static BinOpr get_binop(const char **s) {
  13.     switch (**s) {
  14.     case '+': ++*s; return OP_ADD;
  15.     case '-': ++*s; return OP_SUB;
  16.     case '*': ++*s; return OP_MUL;
  17.     case '/': ++*s; return OP_DIV;
  18.     case '%': ++*s; return OP_MOD;
  19.     case '^': ++*s; return OP_POW;
  20.     default: return OP_NON;
  21.     }
  22. }

  23. static double doexpr(int op, double a, double b) {
  24.     switch (op) {
  25.     case OP_ADD: return a+b;
  26.     case OP_SUB: return a-b;
  27.     case OP_MUL: return a*b;
  28.     case OP_DIV: return a/b;
  29.     case OP_MOD: return a-floor(a/b)*b;
  30.     case OP_POW: return pow(a, b);
  31.     default: return 0;
  32.     }
  33. }

  34. typedef struct ExprContext {
  35.     jmp_buf jbuf;
  36.     const char *errmsg, *s;
  37.     BinOpr op;
  38. } Expr;

  39. static double error(Expr *e, const char *msg) {
  40.     e->errmsg = msg;
  41.     longjmp(e->jbuf, 1);
  42. }

  43. static double expr(Expr *e, int limit) {
  44.     double n;
  45.     BinOpr op;
  46.     if (*e->s == '-') {
  47.         ++e->s;
  48.         n = -expr(e, UNARY_PRIO);
  49.     }
  50.     else if (*e->s == '(') {
  51.         ++e->s;
  52.         n = expr(e, 0);
  53.         if (*e->s++ != ')') error(e, "')' expected");
  54.     }
  55.     else {
  56.         const char *s = e->s;
  57.         n = strtod(s, (char**)&e->s);
  58.         if (e->s == s) error(e, "'number' expected");
  59.     }
  60.     op = get_binop(&e->s);
  61.     while (op != OP_NON && binop_prio[op].left > limit) {
  62.         n = doexpr(op, n, expr(e, binop_prio[op].right));
  63.         op = e->op;
  64.     }
  65.     e->op = op;
  66.     return n;
  67. }

  68. double calc(const char *s, const char **perr) {
  69.     Expr e;
  70.     e.s = s;
  71.     e.errmsg = NULL;
  72.     if (setjmp(e.jbuf) == 0) {
  73.         double n = expr(&e, 0);
  74.         if (*e.s != '\n' && *e.s != '\0' && *e.s != '=')
  75.             error(&e, "traling chars detected");
  76.         return n;
  77.     }
  78.     if (perr) *perr = e.errmsg;
  79.     return 0;
  80. }

  81. int main(void) {
  82.     char buff[BUFSIZ];
  83.     while (printf("> "), fgets(buff, BUFSIZ, stdin) != NULL) {
  84.         const char *errmsg = NULL;
  85.         double n = calc(buff, &errmsg);
  86.         if (errmsg) printf("ERROR: %s\n", errmsg);
  87.         else printf("%g\n", n);
  88.     }
  89.     return 0;
  90. }
复制代码

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
4 [报告]
发表于 2013-03-17 11:40 |只看该作者
后续遍历,  这是<竞赛经典入门>里建表达式树的东西.

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
5 [报告]
发表于 2013-03-17 12:42 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
6 [报告]
发表于 2013-03-17 20:23 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
7 [报告]
发表于 2013-03-18 11:04 |只看该作者
回复 6# pmerofc


    这是简单语言。实用一些的想避免大函数都不行,几十个运算符,光switch case都要写几十上百行。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
8 [报告]
发表于 2013-03-18 11:09 |只看该作者
回复 7# sonicling


    你见过几十个运算符的语言么?C?那也才三十多个,没有上百行吧?

而且,其实很多switch-case是可以被归结成一个数据表的啊。

说到最后,其实还是设计上的问题。我上面的帖子最开始其实是两个函数simpleexpr和expr,是觉得simpleexpr只用了一次而已,就人肉内联到expr了而已。

Lua语言,这是真实语言了吧,它的lparser里面就根本没有大函数的。

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
9 [报告]
发表于 2013-03-18 11:25 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
10 [报告]
发表于 2013-03-18 11:36 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP