鱼C论坛

 找回密码
 立即注册
查看: 2506|回复: 0

[技术交流] 没事做写个有理数计算

[复制链接]
发表于 2014-8-21 23:40:56 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
      前段时间看SICP比较有感觉,也把书上的一些作业实现了一遍。今天不明觉厉突然想用C实现一个计算有理数算式的程序,也算是增强一下自己对代码对程序的控制能力。

     程序的目的是计算形如 1|2*(3+3|4)这样的表达式,程序里为了区分除号和分数线,分数线用 | 表示。

     为了增加程序可读性和模块性,没有在一个文件里实现所有功能模块,整个程序分成了几个部分:
        1、字符串分析部分
        2、常用例程部分
        3、求值部分

     其实这个程序就是一个小型的解释器,所有代码加起来大概600行左右。 字符串分析也就是一个词法扫描器,把 输入的字符串 转化成一个一个的节点,节点内容可能是 一个有理数,比如 1|2,可能是一个运算符,比如+ - 等,或者是一个括号,所以就把这个节点定义为以下数据结构:

typedef enum{
    PARENTHESE,OPERATOR,RAT
}obj_type;

typedef struct obj{
     obj_type type;
     union{
           struct{
               char value;
                     }ch;               //运算符和括号类型
           struct{
                int numer; 
                int denom;
                     }rat;              // 有理数类型,包括分子分母
                }data;
}obj;
    词法扫描部分用了一点自动机理论,毕竟在字符串扫描方面,正则表达式和自动机理论是最有力的武器。词法扫描代码有大概150行左右,就不贴上来了,下面附件有整个代码的下载,总之呢,词法分析生成一个链表,链表的节点就是算术表达式的每一个单词元素。

      现在有了表达式的抽象表达而不是字符串,我们就可以考虑对其进行求值了,我大概只能想出两种办法,一种是转化为后缀表达式然后通过栈求值;还有就是生成语法树,然后遍历语法树得到结果。思考再三还是采用了大家都了解的逆波兰表达式求值,毕竟鱼哥专门在数据结构里面讲过这个。
      求值过称不复杂,只需要维持两个栈,一个用来存储操作数,一个存储操作符和括号。 具体步骤如下:

               1、遍历链表的每个节点
               2、若节点为有理数,则压入操作数栈rat_stack
               3、若不为有理数,则需进行判断,若为+ 或 -,只要当前栈顶不是前括号,则弹出两个操作数和一个操作符进行运算,结果压入操作数栈。
                        然后再将读取到的操作符压入操作符栈
               4、若为 * 或 /,则判断栈顶,若为 * /则如步骤3做一次运算,再将本运算符压栈。
               5、( 直接压栈
               6、若为后括号,则一直重复步骤3中的运算过程,知道栈顶为前括号,然后扔掉前括号
               7,最后检查操作符栈是否为空,不为空则重复步骤三运算步骤知道为空

最后操作数栈顶即为结果。。

     最后一个模块是有理数四则运算,举一例足矣:
obj * rat_add(obj * arg1,obj *arg2)
{
        int n1,n2,d1,d2,n,d;
        obj * result = (obj *)malloc(sizeof(obj));
/*   n1,n2,d1,d2分别为第一个和第二个参加运算的有理数对象的分子和分母  */
        n1 = get_numer(arg1);                     
        n2 = get_numer(arg2);
        d1 = get_denom(arg1);
        d2 = get_denom(arg2);
       
        result->type = RAT;
        n = n1*d2+n2*d1;
        d = d1*d2;
        set_numer(result,n/gcd(n,d));      //约分
        set_denom(result,d/gcd(n,d));
        return result;
}


最后贴上主函数:
int main()
{
        char exp[1024];

        printf("输入表达式:\n>");
        get_exp(exp);
        print(cal(parse(exp)));

        return 0;
}


     水平很菜,发帖完全是为了 巩固自己所学的知识。SICP书中有句话讲得很好:计算机程序是写给人看的,而恰好能被计算机执行。这跟当时这本书出版的时代的主流论点完全相反,但是今天的时代证明这句话是对的。程序的可读性已经超过了效率的重要性。因为计算机硬件越来越快,所以牺牲可读性换取那一点效率是不明智的。从这本书中,我学到很多,他告诉我软件的本质就是抽象,就是模拟。
    不管怎么说,这个帖子也算编辑完了,写得好坏也变得不那么重要了,重要的是通过一个一个的抽象系统的设计,编程水平正在快速的提高
ratcal.rar (196.48 KB, 下载次数: 0)

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-24 20:35

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表