表达式求值
任何一个表达式都是由操作数(operand)运算符(operator)和界限符(delimiter)组成的,统称它们为单词。一般地,操作数既可以是常数,也可以是被说明为变量或常量的标识符;运算符可以分为算术运算符、关系运算符和逻辑运算符 3 类;基本界限符 有左右括号和表达式结束符等。为了叙述的简洁,在此仅讨论简单算术表达式的求值问题,这种表达式只含加、减、乘、除4种运算符。读者不难将它推广到更一般的表达式上。
下面把运算符和界限符统称为算符。
我们知道,算术四则运算遵循以下 3条规则:
(1)先乘除,后加减;
(2)从左算到右;
(3)先括号内,后括号外。
根据上述 3条运算规则,在运算的每一步中,任意两个相继出现的算符θ1和θ2之间的优先关系,至多是下面 3 种关系之一 :
θ1 < θ2 θ1的优先权低于θ2
θ1 = θ2 θ1的优先权等于θ2
θ1 > θ2 θ1的优先权高于θ2
表 3.1 定义了算符之间的这种优先关系。

由规则(1), 先进行乘除运算,后进行加减运算,所以有 “+” < “*”; “+” < “/”; “*” >"+"; “/” > “+” 等。
由规则(2), 运算遵循左结合性,当两个运算符相同时,先出现的运算符优先级高,所以有"+" > “+”; “-” > “-”; “*” > “*”; “/” > “/”。
由规则(3), 括号内的优先级高,+、-、*和/为θ1时的优先性均低千 (" “但高于 " )”。
表中的 “(” = “)” 表示当左右括号相遇时,括号内的运算已经完成。为了便千实现,假设每个表达式均以"#“开始,以”#" 结束。所以"#" = “#” 表示整个表达式求值完毕。")“与 “(”、"#”与”)" 以及"(“与”#" 之间无优先关系,这是因为表达式中不允许它们相继出现,一旦遇到这种情况,则可以认为出现了语法错误。在 下面的讨论中,我们暂假定所输人的表达式不会出现语法错误。
为实现算符优先算法,可以使用两个工作栈,一个称做OPTR,用以寄存运算符;另一个称作OPND, 用以寄存操作数或运算结果。
1.初始化OPTR栈和OPND栈,将表达式起始符“#”压入OPTR栈。
2.扫描表达式,读人第一个字符ch,如果表达式没有扫描完毕至“#”或OPTR的栈顶元素不为“#”时,则循环执行以下操作:
若ch不是运算符,则压入OPND栈,读入下一字符ch;
若ch是运算符,则根据OPTR 的栈顶元素和ch的优先级比较结果,做不同的处理:
若是小于,则ch 压入OPTR栈,读入下一字符ch;
若是大于,则弹出OPTR栈顶的运算符,从 OPND栈弹出两个数,进行相应运算,结果压入OPND栈;
若是等于,则OPTR 的栈顶元素是“(”且ch是“)”,这时弹出OPTR栈顶的“(”,相当于括号匹配成功,然后读人下一字符ch。
3.OPND栈顶元素即为表达式求值结果,返回此元素。
char EvaluateExpression ()
{//算术表达式求值的算符优先算法,设OPTR和OPND分别为运算符栈和操作数栈
InitStack(OPND); //初始化OPND栈
InitStack(OPTR); //初始化OPTR栈
Push (OPTR,'#') ; // 将表达式起始符"#" 压人OPTR栈
cin>>ch;
while(ch!='#' | | GetTop(OPTR) !='#' ) //表达式没有扫描完毕或OPTR的栈顶元素不为"# "
{
if (!In (ch)) {Push (OPND, ch); cin»ch;} //ch不是运算符则进OPND栈
else
switch (Precede (GetTop (OPTR) , ch)) //比较OPTR的栈顶元素和ch的 优先级
{
case'<':
Push(OPTR,ch);cin>>ch; //当前字符ch压入OPTR栈,读入下一字符ch
break;
case'>':
Pop(OPTR,theta); //弹出OPTR栈顶的运算符
Pop(OPND,b);Pop(OPND,a); //弹出OPND栈顶的两个运算数
Push (OPND, Operate (a, theta, b·)); / /将运算结果压入OPND栈
break;
case'=': //OPTR的栈顶元素是"("且ch是")"
Pop(OPTR,x) ;cin>>ch; //弹出OPTR栈顶的"(", 读入下一字符ch
break;
}//switch
}//while
return GetTop (OPND) ; //OPND栈顶元素即为表达式求值结果
}
算法调用的三个函数需要读者自行补充完成。其中函数In是判定读入的字符ch是否为运算符,Precede 是判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数,Operate为进行二元运算的函数。
Window10操作系统,Microsoft Visual C++2010学习版 集成开发环境,C语言
另外需要特别说明的是,上述算法中的操作数只能是一位数,因为这里使用的OPND栈是字符栈,如果要进行多位数的运算,则需要将OPND栈改为数栈,读入的数字字符拼成数之后再入栈。 读者可以改进此算法,使之能完成多位数的运算。
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100 //顺序栈存储空间的初始分配址
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
typedef char SElemType;
typedef struct
{
char *base; //栈底指针
char *top; //栈顶指针
int stacksize; //栈可用的最大容量
}SqStack;
Status InitStack(SqStack *S);//构造一个空栈s
Status Push(SqStack *S,char e);//插入元素e为新的栈顶元素
Status Pop(SqStack *S,char *e);//删除s的栈顶元素,用e返回其值
SElemType GetTop(SqStack S);//返回s的栈顶元素,不修改栈顶指针
Status In(char e);//判断读入字符是否为运算符
SElemType Precede(char a,char b);//比较运算符的优先级,a为纵轴值,b为横轴值
int Operate(int i,char theta,int j);//计算a(theta)b结果
char EvaluateExpression();//算术表达式求值的算符优先算法,设OPTR和OPND分别为运算符栈和操作数栈
int main()
{
printf("请输入算术表达式,并以#结束(操作数只能是一位数):");
printf("表达式结果是:%d",EvaluateExpression());
return 0;
}
Status InitStack(SqStack *S)
{//构造一个空栈s
S->base=(char *)malloc(MAXSIZE*sizeof(char));//为顺序栈动态分配一个最大容量为MAXSIZE的数组空间
if(!S->base) exit(OVERFLOW); //存储分配失败
S->top=S->base; //top初始为base,空栈
S->stacksize=MAXSIZE; //stacksize置为栈的最大容量MAXSIZE
return OK;
}
Status Push(SqStack *S,char e)
{//插入元素e为新的栈顶元素
if(S->top-S->base==S->stacksize) return ERROR; //栈满
*S->top++=e; //元素e压入栈顶,栈顶指针加1
return OK;
}
Status Pop(SqStack *S,char *e)
{//删除s的栈顶元素,用e返回其值
if(S->top==S->base) return ERROR; //栈空
*e=*--S->top; //栈顶指针减1,将栈顶元素赋给e
return OK;
}
SElemType GetTop(SqStack S)
{//返回s的栈顶元素,不修改栈顶指针
if(S.top!=S.base) //栈非空
return *(S.top-1); //返回栈顶元素的值,栈顶指针不变
}
Status In(char e)
{//判断读入字符是否为运算符
if(e=='+'||e=='-'||e=='*'||e=='/'||e=='('||e==')'||e=='#')
return OK;//是
else
return ERROR;//不是
}
SElemType Precede(char a,char b)
{//比较运算符的优先级,a为纵轴值,b为横轴值
char f;
if(a=='+'||a=='-')
{
if(b=='+'||b=='-'||b==')'||b=='#')
f='>';
else if(b=='*'||b=='/'||b=='(')
f='<';
}
else if(a=='*'||a=='/')
{
if(b=='+'||b=='-'||b=='*'||b=='/'||b==')'||b=='#')
f='>';
else if(b=='(')
f='<';
}
else if(a=='(')
{
if(b=='+'||b=='-'||b=='*'||b=='/'||b=='(')
f='<';
else if(b==')')
f='=';
}
else if(a==')')
{
if(b=='+'||b=='-'||b=='*'||b=='/'||b==')'||b=='#')
f='>';
}
else if(a=='#')
{
if(b=='+'||b=='-'||b=='*'||b=='/'||b=='(')
f='<';
else if(b=='#')
f='=';
}
return f;
}
int Operate(int i,char theta,int j)
{//计算a(theta)b结果
int result;
switch(theta) {
case '+': result = i + j; break;
case '-': result = i - j; break;
case '*': result = i * j; break;
case '/': result = i / j; break;
}
return result;
}
char EvaluateExpression()
{//算术表达式求值的算符优先算法,设OPTR和OPND分别为运算符栈和操作数栈
SqStack OPND,OPTR;
int ch; //把读入的字符转换为整数型,即ASCII表值
char a,b,theta,x; //ch为当前读入字符, theta为运算符,x仅仅只是变量寄存弹出值,对计算表达式无影响
InitStack(&OPND); //初始化OPND栈,寄存操作数和运算结果
InitStack(&OPTR); //初始化OPTR栈,寄存运算符
Push(&OPTR,'#');
ch=getchar();
while(ch!='#'||GetTop(OPTR)!='#')
{
printf(" %c\n",ch);
if(!In(ch))//ch不是运算符则进OPND栈
{
ch=ch-48;//数字字符转换为对应整数
Push(&OPND,ch);
ch=getchar();
}
else
{
switch(Precede(GetTop(OPTR),ch))
{//优先级选择
case '<':
Push(&OPTR,ch);
ch=getchar();
break;
case '>':
Pop(&OPTR,&theta);
Pop(&OPND,&b);
Pop(&OPND,&a);
Push(&OPND,Operate(a,theta,b));
break;
case '=':
Pop(&OPTR,&x);
ch=getchar();
break;
}
}
}
return GetTop(OPND);
}
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------