QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 1950|回复: 4

我写的解析表达是程序(计算器)的源代码

[复制链接]
发表于 2004-1-19 21:06:26 | 显示全部楼层 |阅读模式
同题,基于分治法实现,能够支持函数、括号
遵循 GPL 发布

dev-c++写成
欢迎大家使用,和提出意见建议

[code:1]
float calculate_exp(const char *str);
[/code:1]

计算表达式的函数,源代码中有说明

[code:1]
/*******************************************************************************
* main.cpp
*
* 功能:主程序源文件
* 作者: cpp <[email protected]>
* 维护:cpp <[email protected]>
*
* 这是 X-Calculator 的一部分,遵循 GNU General Public Lisence (GPL) 发布
* 任何人可以发 email 给作者,以取得最新的源代码
*******************************************************************************/

#include <iostream>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

/*******************************************************************************
*
* 计算表达式
*
* 参数
* const char                *str:        指向表达式字符串的指针
*
* 返回值:                运算结果
*
*******************************************************************************/
float        calculate_exp(const char *str);

/*******************************************************************************
* 计算两个数的和/差/积/商
*
* 参数
* float        num1:        第一个数
* float        num2:        第二个数
* char                op:                运算符,可以为 '+' '-' '*' '/'
*
* 返回值:                运算结果
*******************************************************************************/
float        calculate_num(float num1, float num2, char op);

/*******************************************************************************
* 计算表达式,与 calculate_str 形成间接递归
*
* 参数
* const char                *str:
*
* 返回值:                运算结果
*******************************************************************************/
float calculate_func(const char *str);

/*******************************************************************************
* 找到最后运算的运算符
*
* 参数
* const char                *str:        指向表达式的字符串
*
* 返回值:                指向最后一个运算符的指针
*******************************************************************************/
const char *        find_last_op(const char *str);

/*******************************************************************************
* 返回一个浮点数的位数 (包含小数点,例如: num_len(1.234) 返回 5)
*
* 参数
* float        n:
*
* 返回值:                位数
*******************************************************************************/
int                num_len(float n);


int main(int argc, char* argv[])
{
        while (1) {
                char str[256];
                cin.getline(str, 256);
                cout << calculate_exp(str) << endl;
        }

        return 0;
}

int num_len(float n)
{
        /*
        * 将 n 转换成字符串,返回该字符串的长度
        */
        char str[256] = {0};
        char *p;

        sprintf(str, "%f", n);
        p = str + strlen(str) - 1;

        while (*p == '0' && p >= str)
                *p-- = 0;

        p = str + strlen(str) - 1;
        if (*p == '.')
                *p = 0;
       
        return strlen(str);
}

const char *find_last_op(const char *str)
{
        int len = strlen(str);
        int        n = 0;
       
        while (len > 0) {
                if ((str[len] == '+' || str[len] == '-') && n == 0)
                        return str + len;
                if (str[len] == '(')
                        n--;
                else if (str[len] == ')')
                        n++;

                len--;
        }

        len = strlen(str);
        n = 0;
        while (len > 0) {
                if ((str[len] == '*' || str[len] == '/') && n == 0)
                        return str + len;
                if (str[len] == '(')
                        n--;
                else if (str[len] == ')')
                        n++;
                len--;
        }
       
        len = strlen(str);
        n = 0;
        while (len > 0) {
                if (str[len] == '^' && n == 0)
                        return str+len;
                if (str[len] == '(')
                        n--;
                else if (str[len] == ')')
                        n++;
                len--;
        }
        return NULL;
}

float calculate_exp(const char *str)
{
        /*
        * 递归调用;
        * 如果 str 不包含运算符,例如 str = "1.1",将 str 转换成小数,返回;
        * 如果 str 包含运算符,找到最后运算的运算符,分成左右两个表达式,
        * 递归调用 calculate (一个表达式可以分成: [左表达式] [运算符] [右表达式]);
        */
        if (find_last_op(str) == NULL && str[0] != '('
                && (str[0] >= '0' && str[0] <= '9' || str[0] == '+' || str[0] == '-')) {        // 中止条件
                return atof(str);
        }
        else if ((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z')
                && find_last_op(str) == NULL) {
                return calculate_func(str);
        }
        else {
                const char        *last_op = find_last_op(str);                // 找到最后运算的运算符
                const char        *p = str;
                int                 n = 0;                // stack

                if (last_op != NULL) {
                        while (p != last_op) {
                                if (*p == '(') n++;
                                else if (*p == ')') n--;
                                p++;
                        }
                }

                if (n != 0 || last_op == NULL) {        // last_op 在括号内 或者 括号内根本没有运算符
                        /*
                        * 脱括号,递归调用
                        */
                        char tmp[256] = {0};
                        strncpy(tmp, str + 1, strlen(str) - 2);
                        return calculate_exp(tmp);
                }
                else {                        // last_op 在括号外
                        /*
                        * 把 last_op 两边的表达式分别计算,在合并结果
                        */
                        char left_str[256] = {0}, right_str[256] = {0};
                        strncpy(left_str, str, last_op - str);
                        strcpy(right_str, last_op + 1);

                        return calculate_num(calculate_exp(left_str),
                                   calculate_exp(right_str), *last_op);
                }
        }

}

float calculate_num(float num1, float num2, char op)
{
        switch (op) {
        case '+':
                return num1 + num2;

        case '-':
                return num1 - num2;

        case '*':
                return num1 * num2;

        case '/':
                return num1 / num2;
       
        case '^':
                return pow(num1, num2);
       
        default:
                return 0;
        }
}

float calculate_func(const char *exp)
{
        char        *l = NULL, *r = NULL;
        char        *p = NULL;
        int                n = 1;        // stack
        float        ret = 0;
        char        str[256] = {0};
       
        strncpy(str, exp, 256);
        l = strchr(str, '(');
        p = l + 1;
        while (*p != 0) {
                if (*p == '(' && n != 0)
                          n++;
                else if (*p == ')' && n != 0)
                          n--;
               
                if (*p == ')' && n == 0) {
                        r = p;
                        break;
                }
                p++;
        }
       
        *r = 0;
        ret = calculate_exp(l+1);
       
        *l = 0;
        if (strcmp(str, "sqrt") == 0)
                return sqrt(ret);
        else if (strcmp(str, "sin") == 0)
                return sin(ret / 360 * 2 * M_PI);
        else if (strcmp(str, "cos") == 0)
                return cos(ret / 360 * 2 * M_PI);
        else if (strcmp(str, "tan") == 0 || strcmp(str, "tg") == 0)
                return tan(ret / 360 * 2 * M_PI);
        else if (strcmp(str, "cot") == 0 || strcmp(str, "ctg") == 0)
                return 1 / tan(ret / 360 * 2 * M_PI);
}


[/code:1]
发表于 2004-1-26 22:47:00 | 显示全部楼层
结构清晰,注释清楚……(优点就不多说了)
说说缺点吧(汗……你等等再把那些东西扔上来好不好……)
嗯……一看就知道:楼主用的是C++
但是从代码中也能看的出来:楼主正在痛苦的从C转向C++(什么?不痛苦?呵呵~但是你的头脑中依然还没有把C和C++区分开)
记得Bjarne Stroustrup曾经说过:把C++当成一门新的语言学习(在他的一篇叫做《将标准C++视为一个新语言》的文章中可以找到)
所以说,并不是说用了#include <iostream>加上class,便是C++了。
那么好,楼主有这样一段代码:
[code:1]
char str&#91;256&#93;;
[/code:1]
另外还有
[code:1]
char *p;
[/code:1]
嗯……Andrew和Moo曾经给学习C++的人一些建议,其中几个是:尽量避免使用指针。尽量使用STL。如果没记错的花,C++提供string来对字符串进行处理,这样就减少了很多程序员的负担。
其他的……偶不说了~呵呵~其实楼主代码总体来说是不错的~
推荐本书:Andrew和Moo一起写的《C++沉思录》
至于《Thinking in C++》《The C++ Programming Language》这些同样经典的书籍我也就不多说了~
回复

使用道具 举报

发表于 2004-1-27 16:32:35 | 显示全部楼层
这是我从Bjarne Stroustrup所著的&lt;&lt;The C++ Programming Language&gt;&gt;里整理的C++风格的一个计算器程序,用gcc编译通过了,至今还没有完全消化理解:)

#include <iostream>
#include &lt;string&gt;
#include &lt;map&gt;
#include &lt;cctype&gt;

using namespace std;

enum Token_value
{
        NAME, NUMBER, END, PLUS='+', MINUS='-',
        MUL='*', DIV='/', PRINT=';', ASSIGN='=',
        LP='(', RP=')'
};
Token_value curr_tok=PRINT;

//declaration of all functions
double expr( bool );  //add and minus
double term( bool );  //divide and multiple
double prim( bool ); //get primer number
Token_value get_token();
double error( const string &amp; );  //error handling

double expr(bool get)
{
        double left=term(get);

        for( ; ; )
                switch(curr_tok)
                {
                        case PLUS:
                                left+=term(true);
                                break;
                        case MINUS:
                                left-=term(true);
                                break;
                        default:
                                return left;
                }
}

double term(bool get)
{
        double left=prim(get);

        for( ; ; )
                switch(curr_tok)
                {
                        case MUL:
                                left*=prim(true);
                                break;
                        case DIV:
                                if(double d=prim(true) )
                                {
                                        left/=d;
                                        break;
                                }
                                return error("divide by 0");
                        default:
                                return left;
                }
}

double number_value;  //global variable declarition
string string_value;
map&lt;string,double&gt; table;

double prim(bool get)
{
        if(get) get_token();

        switch(curr_tok)
        {
                case NUMBER:
                {
                        double v=number_value;
                        get_token();
                        return v;
                }
                case NAME:
                {
                        double &amp;v=table[string_value];
                        if( get_token() == ASSIGN)  v=expr(true);
                        return v;
                }
                case MINUS:
                        return -prim(true);
                case LP:
                {
                        double e=expr(true);
                        if( curr_tok != RP )  return error(") expected");
                        get_token();
                        return e;
                }
                default:
                        return error("primary expected");
        }
}

Token_value get_token()
{
        char ch=0;
        cin &gt;&gt; ch;

        switch( ch )
        {
                case 0:
                        return curr_tok=END;
                case ';':
                case '*':
                case '/':
                case '+':
                case '-':
                case '(':
                case ')':
                case '=':
                        return curr_tok=Token_value(ch);
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                case '.':
                        cin.putback(ch);
                        cin &gt;&gt; number_value;
                        return curr_tok=NUMBER;
                default:
                        if( isalpha(ch) )
                        {
                                cin.putback(ch);
                                cin &gt;&gt; string_value;
                                return curr_tok=NAME;
                        }
                        error("bad token");
                        return curr_tok=PRINT;
        }
}

int no_of_errors;

double error(const string &amp; s)
{
        no_of_errors++;

        cerr&lt;&lt;"error: "&lt;&lt;s&lt;&lt;'\n';
        return 1;
}

int main()
{
        table["pi"]=3.1415926535897932385;
        table["e"]=2.7182818284590452354;

        while(cin)
        {
                get_token();
                if( curr_tok == END ) break;
                if( curr_tok == PRINT ) continue;
                cout&lt;&lt;expr(false)&lt;&lt;'\n';
        }

        return no_of_errors;
}
回复

使用道具 举报

发表于 2004-1-29 14:37:37 | 显示全部楼层
Bjarne Stroustrup just implement an abstract syntax tree, and is more clear than the one wrote by ShiChao.

BTW, never argue about C and C++, no meaning.
回复

使用道具 举报

 楼主| 发表于 2004-2-1 19:27:19 | 显示全部楼层
[quote:f2c0908bf3="默难"]结构清晰,注释清楚……(优点就不多说了)
说说缺点吧(汗……你等等再把那些东西扔上来好不好……)
嗯……一看就知道:楼主用的是C++
但是从代码中也能看的出来:楼主正在痛苦的从C转向C++(什么?不痛苦?呵呵~但是你的头脑中依然还没有把C和C++区分开)
记得Bjarne Stroustrup曾经说过:把C++当成一门新的语言学习(在他的一篇叫做《将标准C++视为一个新语言》的文章中可以找到)
所以说,并不是说用了#include <iostream>加上class,便是C++了。
那么好,楼主有这样一段代码:
[code:1]
char str&#91;256&#93;;
[/code:1]
另外还有
[code:1]
char *p;
[/code:1]
嗯……Andrew和Moo曾经给学习C++的人一些建议,其中几个是:尽量避免使用指针。尽量使用STL。如果没记错的花,C++提供string来对字符串进行处理,这样就减少了很多程序员的负担。
其他的……偶不说了~呵呵~其实楼主代码总体来说是不错的~
推荐本书:Andrew和Moo一起写的《C++沉思录》
至于《Thinking in C++》《The C++ Programming Language》这些同样经典的书籍我也就不多说了~[/quote]

谢谢提出的建议啊
用STL里面的string,总感觉别扭,感觉iterator没有指针用的爽
回复

使用道具 举报

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

本版积分规则

GMT+8, 2024-11-9 00:43 , Processed in 0.044071 second(s), 16 queries .

© 2021 Powered by Discuz! X3.5.

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