回 帖 发 新 帖 刷新版面

主题:[讨论]我见过最牛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个回复)

21 楼


就一个字  很晕

22 楼

先站个位置

有时间读下

23 楼

我还要好好学习,这些代码是我平时写的几倍多...

24 楼

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

不敢苟同!

本人认为要发展中国的电子技术,或者数子技术就应该究其本源.

计算机做数值计算是有误差的,
Matlab 矩整(线形数学计算接口)环境 把稍底层的误差处理做好了给一个接口,我们能在其它的编程语言中调用。

还有一些图形程序接口,也是把底层的误差处理做好了给一个接口,我们能在其它的编程语言中调用。

我们只不过在做着,被人家自然化了的顶层的最简单的事情。基础人家打的,人家换一次标准,我们就的花费很大的资金,资源来适应人家的标准。

如我们要发展自己的数子技术,就应该像底层发展,越底层越好,底层到电路:
Processing Unit的电路是如何让这样的一串脉冲高效变为那样的一串脉冲的,如何将一个电位无衰减的存储于内存的某一电学单元,又是如何被某样的一串脉冲激发出来,并传输到目的电学单元

只有向底层发展我们才能真正的独立自主,才会出现自己的CPU而不是引进AMD的过时半个世纪的技术,来生产出自己的龙芯,汉芯CPU。
才能让自己研发的超级计算机用上独立自主的CPU ,而不是AMD TNTEL

25 楼

编译原理的话,要展现切词(字典)、语法树--这里面没有
数据结构的话,要展现好的字典结构,好的优先级处理结构如栈,边解析边计算,不是全部计算符存好再来算--这里也没怎么吧
c语言编程的话,好强啊!
杀鸡,用刀就行;杀一个敌人,用手枪可以了;杀一个军队,只好把坦克开出来。杀一个军队,拿只小刀就过去了,如果做到那一定很强很爽。

26 楼

其实我是用FORTRAN的,C只是略知一二

是你们的版主叫我把这篇文章贴上来的

matlab 是商业软件,要用钱买的

原来编译器是这样工作的

27 楼


果然牛B!厉害厉害!看来我还是不要学C语言了[em3]

我来回复

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