回 帖 发 新 帖 刷新版面

主题:[讨论]我见过最牛B的程序

//     §1  一个用于数学函数值计算的C函数 — 求任意数学函数f(x)和f(x,y)的值

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//    任意输入一个数学函数的表达式,在输入自变量的值后,计算出数学函数表达式的值,这在很多情况下都会遇到。例如,一元函数y=2sinx+1,求x=5时的函数值y;或二元函数z=3cos(x+1)+y,求x=2,y=3时的函数值z;或三元函数q=2x+3y+sin(z+2x), 求x=1,y=2,z=3时的函数值q。
//    以下程序中的三个C函数float f(float x) 、float f(float x,float y)和float f(float x,float y,float z) 分别实现一元函数、二元函数和三元函数值的计算(因为这三个C函数的名字相同,都为f,只是参数个数不同,这用到了C++中函数重载的功能,所以要用C++编绎,即源程序文件的扩展名应取CPP)。
//    程序实现中用到了“编绎原理”中有关表达式编绎的知识,包括表达式的词法分析、语法分析和语义生成,有兴趣的读者请参阅有关书籍。
//    程序运行后,数学函数的输入格式采用C语言算术表达式的格式,例如对于一元函数y=2sinx+1,则输入2*sin(x)+1;对于二元函数z=3cos(x+1)+y,则输入3*cos(x+1)+y;对于三元函数q=2x+3(y-2)+sin(z+x),则输入2*x+3*(y-2)+sin(z+2*x)等等。
//    需要提醒读者注意的是,本程序的语法分析功能极其有限,仅仅提供了一个语法分析的C函数接口,有兴趣的读者可以自己添加语法分析代码。所以对错误的C语言表达式输入,大多不能报错。因此,使用时务必输入正确的C语言表达式。
//    下列程序是实现C语言算术表达式计算的函数,文件名取为 expressi .cpp。使用方式有二种,一是加入到读者的project 中;二是用 #include  "expressi.cpp" 语句包含到读者的源程序文件中。本章的范例程序采用后者。所以,如果范例程序中有 #include  "expressi.cpp" 语句,则读者应首先把以下程序输入到计算机中,并用文件名 expressi.cpp 存盘。
//
//////////////////////////////////////////////////////////////////////////////////////////////////
//计算C语言算术表达式程序,用于一元或多元函数值的计算
//

#include    <stdio.h>
#include    <conio.h>
#include    <math.h>
#include    <string.h>

#define    ADD    0xff01
#define    SUB    0xff02
#define    MUL    0xff03
#define    DIV    0xff04
#define LEFT_PARENTHESES    0xff05
#define RIGHT_PARENTHESES    0xff06
#define COMMA    0xff07

#define ADD1    0xff07
#define SUB1    0xff08
#define EQU    0xff09

#define    SIN    0xff10
#define    COS    0xff11
#define    TAN    0xff12
#define    ASIN    0xff13
#define ACOS    0xff14
#define ATAN    0xff15
#define EXP    0xff16
#define LOG    0xff17
#define POW     0xff18
#define SQRT    0xff19
#define FABS    0xff1a
#define FACTORIAL    0xff1b
#define MINUS    0xff1c

struct OPERATOR_FUNCTION_NAME_LIST{
    char Name[32]; //操作符字符串名字
    int Code;      //代码
    int Pri;       //优先级
    int NumOfOper; //操作数个数,即是几元操作符
}OF_Name[]={
    {"+",    0xff01,1,2},
    {"-",    0xff02,1,2},
    {"*",    0xff03,2,2},
    {"/",    0xff04,2,2},
    {"(",    0xff05,4,0},
    {")",    0xff06,0,0},
    {",",    0xff07,0,0},

    {"sin",    0xff10,3,1},
    {"cos",    0xff11,3,1},
    {"tan",    0xff12,3,1},
    {"asin",0xff13,3,1},
    {"acos",0xff14,3,1},
    {"atan",0xff15,3,1},
    {"exp",    0xff16,3,1},
    {"log",    0xff17,3,1},
    {"pow",    0xff18,3,2},
    {"sqrt",0xff19,3,1},
    {"fabs",0xff1a,3,1},
    {"factorial",    0xff1b,3,1},
    {"minus",0xff1c,5,1},
    {"",0,0}
};

//阶乘函数
float Factorial(float n)
{
    float Result,ftmp;

    ftmp=n;
    Result=1.;
    while(ftmp>1.)
    {
        Result*=ftmp;
        ftmp-=1.;
    }
    return(Result);
}

//表达式字符串中下一个单词长度
int NextWordLen(char e[])
{
    int i;

    if(e[0]=='+'||e[0]=='-'||e[0]=='*'||e[0]=='/'||
        e[0]=='('||e[0]==')'||e[0]==',')
        return 1;
    else
    {
        i=0;
        do{
            ++i;
        }while(e[i]!='+'&&e[i]!='-'&&e[i]!='*'&&e[i]!='/'&&
                e[i]!='('&&e[i]!=')'&&e[i]!=','&&e[i]!='\0');
        return i;
    }
}

//返回单词在操作符名字表中的下标值
int Check_OF_Name_List(char Word[])
{
    int i;

    i=0;
    while(OF_Name[i].Name[0]!=0)
    {
        if(strcmp(Word,OF_Name[i].Name)==0)
            return(OF_Name[i].Code); //注意,返回的是负数,请看前面OF_Name[i].Code中定义
        ++i;
    }

    return 1;
}

//返回单词在变量名表中下标,如果表中还没有,则加入这个单词
int CheckOrAddVarNameList(char Word[],char VarNameList[][32])
{
    int i;

    i=0;
    while(VarNameList[i][0]!=0)
    {
        if(strcmp(Word,VarNameList[i])==0)
            return(i);
        ++i;
    }

    strcpy(VarNameList[i],Word);
    VarNameList[i+1][0]='\0';

    return(i);
}

//格式化表达式,如单词用固定长的int数来表示
int GetFormated_C_Expression(char C_Expression[],int Fmt_C_Exp[],
                                char VarNameList[][32])
{
    int i,i1,j,WordLen;
    char Word[32];

    i=0;
    j=0;
    while(C_Expression[i]!=0)
    {
        WordLen=NextWordLen(&C_Expression[i]);//取得下一个单词长度
        strncpy(Word,&C_Expression[i],WordLen); //取出单词
        Word[WordLen]='\0';

        i1=Check_OF_Name_List(Word); //检查操作符表,返回下标
        if(i1<0)
        {   //单词是一个操作符

            Fmt_C_Exp[j] = i1;
            if(i1==SUB&&(Fmt_C_Exp[j-1]==LEFT_PARENTHESES||Fmt_C_Exp[j-1]==COMMA))
                Fmt_C_Exp[j]=MINUS; // "-" 是不是一个负号而不是减号
        }
        else
            Fmt_C_Exp[j]=CheckOrAddVarNameList(Word,VarNameList); //该单词是一个变量名,则放入变量名字表
        ++j;

        i+=WordLen;//下一个单词起始位置
    }
    Fmt_C_Exp[j]=0xffff; //结束标志
    return 0;
}

//返回操作符优先级
int OperNum(int Code)
{
    int i;

    i=0;
    while(OF_Name[i].Code!=0)
    {
        if(Code==OF_Name[i].Code)
            return(OF_Name[i].NumOfOper);
        ++i;
    }
    return 0;
}

//表达式是否符合C语言算术表达式,本函数有待改进,按理说用边词法分析边进行语法分析和检查
//比较好,但现在是事后分析一下有无错误,所以这一程序还不大经典。这是后来发现这一缺点。
int IsValidExpression(int Fmt_C_Exp[],char VarNameList[][32])
{
    int i,Valid,Parentheses;

    Parentheses=0;
    Valid=0;

    i=0;
    while(Fmt_C_Exp[i]!=0xffff)
    {
        if(((Fmt_C_Exp[i]>=0xff01&&Fmt_C_Exp[i]<=0xff04)||Fmt_C_Exp[i]==MINUS)&&
           (((Fmt_C_Exp[i-1]>=0xff01&&Fmt_C_Exp[i-1]<=0xff04)||Fmt_C_Exp[i-1]==MINUS)||
            ((Fmt_C_Exp[i+1]>=0xff01&&Fmt_C_Exp[i+1]<=0xff04)||Fmt_C_Exp[i+1]==MINUS)))
            Valid=1;
        if(Fmt_C_Exp[i]==LEFT_PARENTHESES)++Parentheses;
        if(Fmt_C_Exp[i]==RIGHT_PARENTHESES)--Parentheses;

        /*Other Invalid Case can be added here Or
        rewrite this function at all by user!,
        Otherwise,You must input a correct C_Expression!*/

        /************************************/

        ++i;
    }
    if(Parentheses!=0)Valid=1;
    return(Valid);
}

//返回优先数
int Pri(int Code)
{
    int i;

    i=0;
    while(OF_Name[i].Code!=0)
    {
        if(Code==OF_Name[i].Code)
            return(OF_Name[i].Pri);
        ++i;
    }
    return 0;
}

//实际得到4元组伪指令代码,用栈来实现的。
int GetOperatorSerials(int Fmt_C_Exp[],int OperatorSerials[][4],int VarP)
{
    int OperP;
    int i;
    int O_Stack[200],O_P; //操作符栈
    int V_Stack[200],V_P; //变量和常数栈

    OperP=0;
    O_P=0;
    V_P=0;
    i=0;
    while(Fmt_C_Exp[i]!=0xffff)
    {
        if(Fmt_C_Exp[i]<2000&&Fmt_C_Exp[i]>=0)
        {
            V_Stack[V_P]=Fmt_C_Exp[i];
            ++V_P;
        }
        else
        {
            if(Fmt_C_Exp[i]!=LEFT_PARENTHESES)
                while(O_P>0&&Pri(Fmt_C_Exp[i])<=Pri(O_Stack[O_P-1]))
                {
                    if(O_Stack[O_P-1]==LEFT_PARENTHESES&&
                        Fmt_C_Exp[i]==RIGHT_PARENTHESES)
                    {
                        --O_P;
                        break;
                    }
                    if(O_Stack[O_P-1]!=LEFT_PARENTHESES)
                    {
                        switch(OperNum(O_Stack[O_P-1]))
                        {
                            case 0:
                                --O_P;
                                break;
                            case 1:
                                OperatorSerials[OperP][0]=O_Stack[O_P-1];
                                OperatorSerials[OperP][1]=V_Stack[V_P-1];
                                OperatorSerials[OperP][3]=VarP;
                                V_Stack[V_P-1]=VarP;
                                ++VarP;
                                ++OperP;
                                --O_P;
                                break;
                            case 2:
                                OperatorSerials[OperP][0]=O_Stack[O_P-1];
                                OperatorSerials[OperP][2]=V_Stack[V_P-1];
                                OperatorSerials[OperP][1]=V_Stack[V_P-2];
                                OperatorSerials[OperP][3]=VarP;
                                V_Stack[V_P-2]=VarP;
                                ++VarP;
                                --V_P;
                                ++OperP;
                                --O_P;
                                break;
                        }
                    }
                    else
                        break;
                }
            if(Fmt_C_Exp[i]!=RIGHT_PARENTHESES)
            {
                O_Stack[O_P]=Fmt_C_Exp[i];
                ++O_P;
            }
        }
        ++i;
    }
    OperatorSerials[OperP][0]=0;
    return 0;
}

//变量名个数
int VarNameListLen(char VarNameList[][32])
{
    int Length;

    Length=0;
    while(VarNameList[Length][0]!='\0') ++Length;
    return(Length);
}

//生成4元组伪指令代码总程序
int MakeFunction(char C_Expression[],int OperatorSerials[][4],
                    char VarNameList[][32])
{
    int Fmt_C_Exp[1000];

    GetFormated_C_Expression(C_Expression,Fmt_C_Exp,VarNameList);
    if(IsValidExpression(Fmt_C_Exp,VarNameList))return 1;

    GetOperatorSerials(Fmt_C_Exp,OperatorSerials,VarNameListLen(VarNameList));

    return 0;
}

回复列表 (共27个回复)

11 楼

[quote]在我的编译器上怎么不好使啊[/quote]

在VC++ 6.0下好象会报错

不过在TC++ 3.0下编译通过

12 楼

本贴点击量突破1000点大关,在此衷心表示感谢!

请大家多多关注!

13 楼

说实话,你这个程序不怎么样。

看了有点伤脑——运算的逻辑纠缠在一起,像一团浆糊。parser 不像parser,evaluator不像evaluator。

建议利用c++ 的多态性质来编写,生产树状结构,而不是平面的,由若干数组互相耦合在一起的数据结构。那样,你这个程序就会看起来好多了。

14 楼

另,全局变量最好用函数包裹起来(文件静态或者函数内静态),以免一荣具荣,一损具损。

15 楼

C++.in.Action中有一个更好的版本,应该比
[quote]
张诚坚、高健、何南忠编写的《计算方法》上的
[/quote]

这个,要好点。

16 楼

吊!!!!!!!!!!!!!

17 楼

mark`~~

18 楼

就是表达式求值是吧。

19 楼

计算方法如果在C编译环境下做实验,只要用函数指针就足够了,为了一个表达式计算写这么份代码不值,也可以直接在matlab下编程

20 楼

呵呵 狠

我来回复

您尚未登录,请登录后再回复。点此登录或注册