数学表达式 (200分)

  • 主题发起人 主题发起人 晓明
  • 开始时间 开始时间

晓明

Unregistered / Unconfirmed
GUEST, unregistred user!
碰到的问题是这样的,在定制报表编辑器中(一维)
模拟了一个快速报表中的数学表达式输入器。
用户输入的表达式作为报表的列数据,
为了加快速度要求以SQL的方式提交到数据库(SYBASE),由数据库进行相应的计算
考虑到语法可能不一样,比如表达式中 add(a,b,c)
在sybase中可能需要变成 a + b + c
语义不同,如 a mod c
sybase a % c
请各位大吓发表意见,提供sybase 全部数学函数
 
还是在客户端把这些数据转换好,并检测及输入的合法性,然后存入数据库。
这样比你现有的方案更合理些。在客户端做这些操作是很快的。
 
不是要更新数据,而是按表达式选出数据 如select field1,field2,表达式 from table
急切需要提供sybase 数学函数 谢谢!
 
不知道符不符和你的要求。
一款设计精巧的表达式解析器
---- 开发MIS系统时,报表设计中经常会碰到表达式解释器,完成用户自定义的公式运算。这种程序的设计需要有比较高的技巧,以下介绍一款用DELPHI4.0开发的程序[程序重在算法,语言特性很少,用其它语言的人也能读懂],只要按自已的要求稍加修改,即可做成组件或全局方法发部。它支持 "加[+]、减[-]、乘[*]、除[/]、商[$:两整数相除,结果的整数部分]、模[%]、括号[()]"四则混合运算,支持"与[&amp;]、或[|]、异或[^]、左移[< ]、右移[ >]和非[!]"逻辑运算功能,同时它们可以出现在同一个表达式中,它们的优先级依次为括号、非、与或异或左右移、乘除商模、加减。如式:12.45+3*16 >2*(3+6*(3+2)-1)=12.45+3*4*32,计算结果为:396.45。程序包括两大部分功能:表达式拆解、因子计算,分别由两个类TBdsProc和TPUSHPOP完成。具体如下:
CDIKind=record
case id: Boolean of
True: (dval:do
uble);
False: (ival: Integer);
end;
CDKind:区别表达式中的整数和浮点数类型,
因为有些运算符不支持浮点数(如逻辑运算)。
ValKind = CDIKind;
TBdsProc = class
private
Fghpd : Integer;//识别并标记左右括号是否成对出现
function IsCalcFh(c: Char): boolean;
//判别一个字符是否运算符
function CopyRight(abds: String;start: Integer):
String;//截取字符串表达式
function BdsSs(var abds: String): ValKind;
//返回一个子表达式的值
function BdsYz(var abds: String): ValKind;
//表达式因子,如:15、(13+5)
function BdsItm(var abds: String): ValKind;
//读取表达式中的一个因子
public
function CalcValue(const bds: String): ValKind;
//返回计算结果
end;

TPUSHPOP = class
private
ffh: array [0..2] of Char;//符号数组
value: array [0..3] of CDIKind;//值数组
flevel: Byte;//因子个数
fisfh: Boolean;//识别等待输入值或运算符
fisnot: Boolean;//识别待计算数据项是否执行非运算
function Calcsj(av1,av2: CDIKind;fh: Char): CDIKind;
//执行两个数值的四则运算
function Calclg(av1,av2: CDIKind;
fh: Char): CDIKind;
//执行两个数的逻辑运算
procedure Calccur;{当输入数据项满足四个数后
[依运算优先级层数求得,见下述算式解析原理]执行中间运算}
function IsLgFh(fh: Char): Boolean;
//一个符号是否逻辑运算符
function IsCcFH(fh: Char): Boolean;
// 一个符号乘除商模运算符
public
constructor Create;
procedure PushValue(avalue: CDIKind);//存入一个数据项
procedure PushFh(afh: Char);//存入一个符号
function CalcValue: CDIKind;//计算并返回值
end;

---- 表达式解析基本原理:
---- 1.表达式处理:
---- 表达式的一个个数据项组成,中间由运算符连接,每个数据项为一个分析基本分析单元。表达式中如果包含有改变运算优先级别的括号运算,先计出括号中式子的值,再把该值当一个数据项处理,这一点在程序设计中只要运用递归功就能实现。
---- 2.数据项计算处理
---- a >非运算:
---- 它为单目运算符,级别最高,在存入符号时做标记,存入数据时即时计算并去除标记。
---- b >表达式运算:
---- 设f1、f2、f3分别表示一二三级运算符,V1、V2、V3、V4分别表示顺序四个数,则极端表达式模型为R=V1 f1 V2 f2 V3 f3 V4 …,计算时顺序应为 R=…V4 f3 V3 f2 V2 f1 V1。为了简化运算,把其中运算级别最高的逻辑运算在存入数据时先计算完成, 初始化时设V1=0,第一个运算符设为'+'。则公式化为: R=…V4 f2(f1) V3 f2(f1) V2 f1 V1。这样,当V2与V3间的运算符级别为f2时,V4与V3间的运算符级别< =f2,则:V2 =(V2与V3计算值),V3后的值和运算符前移;若V2与V3间的运算级别为f1,可先算V1与V2,V2以后的值和运算符前移。则计算后的表达式为:R=V3 f2(f2) V2 f1 V1刚好满足循环取数条件。
---- 3.实现:
---- 程序比较长(TBdsProc和TPUSHPOP的源代码合计长度为400多行),完整代码见附件,以下对一些重要实现方法做介绍:
---- < 1 >表达式拆解:由方法BdsSs和BdsYz完成表达式拆解和因子处理
function TBdsProc.BdsSs(var abds: String): ValKind;
var
c: Char;
lpp: TPushPop;
begin
lpp := TPushPop.Create;//建立数据计算对象
while abds< >''do
begin
c := abds[1];
if IsCalcFh(c) then
//是否运算符
begin
lpp.PushFh(c);//保存运算符
abds := CopyRight(abds,2);
end
else
begin
if c=')' then
begin
Dec(Fghpd);//括号匹配
abds := CopyRight(abds,2);
if Fghpd < 0 then
Raise Exception.Create('括号不配对');
Result := lpp.CalcValue;
//返回括号中的子项值,进行下一步计算
lpp.Free;
Exit;
end
else
begin
if c='(' then
Inc(Fghpd);//做括号层数标识
lpp.PushValue(BdsYz(abds));//取下一项的值。
end;
end;
end;
if Fghpd< >0 then
Raise Exception.Create('括号不配对');
Result := lpp.CalcValue;//返回最终运算值
lpp.Free;
end;

function TBdsProc.BdsYZ(var abds: String): ValKind;
begin
if abds< >'' then
begin
if abds[1]='(' then
begin
abds := CopyRight(abds,2);
Result := BdsSs(abds);//递归调用,求括号中的值
end
else
Result := BdsItm(abds);{读一个数据项,
如果包括函数定义,可以在该方法中定义接口或连接}
end;
end;
若表达式要支持函数功能,只要定义系统支持的函数,
在取行表达式因子BdsItm中加入连接函数定义即可,如下:
…..
else
if (c< ='Z') and (c >='A') then
begin
bhs := 取出函数名及参数
Result := … //调用函数处理 (函数定义)
abds := 下一项
end
….
< 2 > 数据计算:主要包PushValue,PushFh,
Calccur分别完成存入数、符号、中间计算
procedure TPUSHPOP.PushValue(avalue: CDIKind);
begin
if fisfh=True then
Raise Exception.Create('缺少运算符');
if fisnot then
//进行非运算
begin
if avalue.id then
Raise Exception.Create('浮点数不能做非运算');
avalue.ival := not avalue.ival;
fisnot := False;
end;
if IsLgFh(ffh[flevel]) then
//运行逻辑运算
begin
value[flevel] := Calclg(value[flevel],
avalue,ffh[flevel]);
//与当前值做逻辑运算
end
else
begin
Inc(flevel);//存数位置指针加1
value[flevel] := avalue;//存入值
if flevel >2 then
//数据个数达到4,进行中间运算
Calccur;
end;
fisfh := True;//输入符号可见
end;

procedure TPUSHPOP.PushFh(afh: Char);
begin
if (fisfh=false) then
//非运算是一级
begin
if (afh='!') and (not fisnot) then
//标识非运算
begin
fisnot := True;
Exit;
end
else
Raise Exception.Create('运算符重复');
End
else
begin
ffh[flevel] := afh;//存入运算符
fisfh := False;
输入值可见
end;
end;

procedure TPUSHPOP.Calccur;
begin
if IsCcFh(ffh[1]) then
//二级运算符
begin
value[1] := Calcsj(value[1],value[2],ffh[1]);
//计算2和3项的值
ffh[1] := ffh[2];//后序运符和值前移
value[2] := value[3];
end
else
//一级运算符
begin
value[0] := Calcsj(value[0],value[1],ffh[0]);
//计算1和2项的值
value[1] := value[2];{ 后序运符和值前移,
2和3项的值不计算是为了保持第一个运算符为一级运算符}
value[2] := value[3];
ffh[0] := ffh[1];
ffh[1] := ffh[2];
end;
Dec(flevel);//存数位置指针减1
end;
 
设f1、f2、f3分别表示一二三级运算符,V1、V2、V3、V4分别表示顺序四个数
,则极端表达式模型为R=V1 f1 V2 f2 V3 f3 V4 …,
计算时顺序应为 R=…V4 f3 V3 f2 V2 f1 V1。为了简化运算,
把其中运算级别最高的逻辑运算在存入数据时先计算完成, 初始化时设V1=0,
第一个运算符设为'+'。则公式化为: R=…V4 f2(f1) V3 f2(f1) V2 f1 V1。
这样,当V2与V3间的运算符级别为f2时,V4与V3间的运算符级别< =f2,
则:V2 =(V2与V3计算值),V3后的值和运算符前移;若V2与V3间的运算级别为f1,
可先算V1与V2,V2以后的值和运算符前移。则计算后的表达式为
:R=V3 f2(f2) V2 f1 V1刚好满足循环取数条件
这一段看不懂?
计算树不是可以解决吗?
 
sysbase中的数学函数如下:
ABS ( numeric-expr )
ACOS ( numeric-expr )
ASIN ( numeric-expr )
ATAN ( numeric-expr )
ATAN2 ( numeric-expr, numeric-expr )
CEILING ( numeric-expr )
COS ( numeric-expr )
COT ( numeric-expr )
DEGREES (numeric-expr )
EXP ( numeric-expr )
FLOOR ( numeric-expr )
LOG ( numeric-expr )
LOG10 ( numeric-expr )
MOD ( dividend, divisor )
PI ( * )
POWER ( numeric-expr, numeric-expr )
RADIANS ( numeric-expr )
RAND ( [integer-expr] )
REMAINDER ( numeric-expr, numeric-expr )
ROUND ( numeric-expr, integer-expr )
SIGN ( numeric-expr )
SIN ( numeric-expr )
SQRT ( numeric-expr )
TAN ( numeric-expr )
"TRUNCATE" ( numeric-expr, integer-expr )
函数描述:
ABS( numeric-expr ) Returns the absolute value of numeric-expr.
ACOS( numeric-expr ) Returns the arc-cosine of numeric-expr in radians.
ASIN( numeric-expr ) Returns the arc-sine of numeric-expr in radians.
ATAN( numeric-expr ) Returns the arc-tangent of numeric-expr in radians.
ATAN2( numeric-expr1, numeric-expr2 ) Returns the arc-tangent of numeric-expr1/numeric-expr2 in radians.
CEILING( numeric-expr ) Returns the ceiling (smallest integer not less than) of numeric-expr.
COS( numeric-expr ) Returns the cosine of numeric-expr, expressed in radians.
COT( numeric-expr ) Returns the cotangent of numeric-expr, expressed in radians.
DEGREES( numeric-expr ) Converts numeric-expr, from radians to degrees.
EXP( numeric-expr ) Returns the exponential function of numeric-expr.
FLOOR( numeric-expr ) Returns the floor (largest integer not greater than) of numeric-expr.
LOG( numeric-expr ) Returns the logarithm of numeric-expr.
LOG10( numeric-expr ) Returns the logarithm base 10 of numeric-expr.
MOD( dividend, divisor ) Returns the remainder when dividend is divided by divisor. Division involving a negative dividend will give a negative or zero result. The sign of the divisor has no effect.
PI( * ) Returns the numeric value PI.
POWER ( numeric-expr1, numeric-expr2 ) Raises numeric-expr1 to the power numeric-expr2.
RADIANS ( numeric-expr ) Converts numeric-expr, from degrees to radians.
RAND ( [ integer-expr ] ) Returns a random number in the interval 0 to 1, with integer-expr as an optional seed.
REMAINDER( dividend, divisor ) Same as the MOD function.
ROUND ( numeric-expr, integer-expr ) Rounds numeric-expr to integer-expr places after the decimal point. A positive integer determines the number of significant digits to the right of the decimal point;
a negative integer, the number of significant digits to the left of the decimal point.
SIGN( numeric-expr ) Returns the sign of numeric-expr.
SIN( numeric-expr ) Returns the sine of numeric-expr, expressed in radians.
SQRT( numeric-expr ) Returns the square root of numeric-expr.
TAN( numeric-expr ) Returns the tangent of numeric-expr, expressed in radians.
"TRUNCATE" ( numeric-expr, integer-expr ) Truncates numeric-expr at integer-expr places after the decimal point. A positive integer determines the number of significant digits to the right of the decimal point;
a negative integer, the number of significant digits to the left of the decimal point.
 
谢谢各位大吓 !!
谢谢 windbell (风铃?) 提供sybase 函数
不过表达式的问题还没有解决,希望个位大吓提供意见??
二叉树不能解决函数的计算(能解决二目计算,二参数函数也凑合),
因为函数中参数的个数并不确定 `^@^
目前还想不出好的数据结构来实现 ....

!!过一段时间后一定发分 ,不管问题如何。
 
一款设计精巧的表达式解析器
---- 开发MIS系统时,报表设计中经常会碰到表达式解释器,完成用户自定义的公式运算。这种程序的设计需要有比较高的技巧,以下介绍一款用DELPHI4.0开发的程序[程序重在算法,语言特性很少,用其它语言的人也能读懂],只要按自已的要求稍加修改,即可做成组件或全局方法发部。它支持 "加[+]、减[-]、乘[*]、除[/]、商[$:两整数相除,结果的整数部分]、模[%]、括号[()]"四则混合运算,支持"与[&amp;]、或[|]、异或[^]、左移[< ]、右移[ >]和非[!]"逻辑运算功能,同时它们可以出现在同一个表达式中,它们的优先级依次为括号、非、与或异或左右移、乘除商模、加减。如式:12.45+3*16 >2*(3+6*(3+2)-1)=12.45+3*4*32,计算结果为:396.45。程序包括两大部分功能:表达式拆解、因子计算,分别由两个类TBdsProc和TPUSHPOP完成。具体如下:
CDIKind=record
case id: Boolean of
True: (dval:do
uble);
False: (ival: Integer);
end;
CDKind:区别表达式中的整数和浮点数类型,
因为有些运算符不支持浮点数(如逻辑运算)。
ValKind = CDIKind;
TBdsProc = class
private
Fghpd : Integer;//识别并标记左右括号是否成对出现
function IsCalcFh(c: Char): boolean;
//判别一个字符是否运算符
function CopyRight(abds: String;start: Integer):
String;//截取字符串表达式
function BdsSs(var abds: String): ValKind;
//返回一个子表达式的值
function BdsYz(var abds: String): ValKind;
//表达式因子,如:15、(13+5)
function BdsItm(var abds: String): ValKind;
//读取表达式中的一个因子
public
function CalcValue(const bds: String): ValKind;
//返回计算结果
end;

TPUSHPOP = class
private
ffh: array [0..2] of Char;//符号数组
value: array [0..3] of CDIKind;//值数组
flevel: Byte;//因子个数
fisfh: Boolean;//识别等待输入值或运算符
fisnot: Boolean;//识别待计算数据项是否执行非运算
function Calcsj(av1,av2: CDIKind;fh: Char): CDIKind;
//执行两个数值的四则运算
function Calclg(av1,av2: CDIKind;
fh: Char): CDIKind;
//执行两个数的逻辑运算
procedure Calccur;{当输入数据项满足四个数后
[依运算优先级层数求得,见下述算式解析原理]执行中间运算}
function IsLgFh(fh: Char): Boolean;
//一个符号是否逻辑运算符
function IsCcFH(fh: Char): Boolean;
// 一个符号乘除商模运算符
public
constructor Create;
procedure PushValue(avalue: CDIKind);//存入一个数据项
procedure PushFh(afh: Char);//存入一个符号
function CalcValue: CDIKind;//计算并返回值
end;

---- 表达式解析基本原理:
---- 1.表达式处理:
---- 表达式的一个个数据项组成,中间由运算符连接,每个数据项为一个分析基本分析单元。表达式中如果包含有改变运算优先级别的括号运算,先计出括号中式子的值,再把该值当一个数据项处理,这一点在程序设计中只要运用递归功就能实现。
---- 2.数据项计算处理
---- a >非运算:
---- 它为单目运算符,级别最高,在存入符号时做标记,存入数据时即时计算并去除标记。
---- b >表达式运算:
---- 设f1、f2、f3分别表示一二三级运算符,V1、V2、V3、V4分别表示顺序四个数,则极端表达式模型为R=V1 f1 V2 f2 V3 f3 V4 …,计算时顺序应为 R=…V4 f3 V3 f2 V2 f1 V1。为了简化运算,把其中运算级别最高的逻辑运算在存入数据时先计算完成, 初始化时设V1=0,第一个运算符设为'+'。则公式化为: R=…V4 f2(f1) V3 f2(f1) V2 f1 V1。这样,当V2与V3间的运算符级别为f2时,V4与V3间的运算符级别< =f2,则:V2 =(V2与V3计算值),V3后的值和运算符前移;若V2与V3间的运算级别为f1,可先算V1与V2,V2以后的值和运算符前移。则计算后的表达式为:R=V3 f2(f2) V2 f1 V1刚好满足循环取数条件。
---- 3.实现:
---- 程序比较长(TBdsProc和TPUSHPOP的源代码合计长度为400多行),完整代码见附件,以下对一些重要实现方法做介绍:
---- < 1 >表达式拆解:由方法BdsSs和BdsYz完成表达式拆解和因子处理
function TBdsProc.BdsSs(var abds: String): ValKind;
var
c: Char;
lpp: TPushPop;
begin
lpp := TPushPop.Create;//建立数据计算对象
while abds< >''do
begin
c := abds[1];
if IsCalcFh(c) then
//是否运算符
begin
lpp.PushFh(c);//保存运算符
abds := CopyRight(abds,2);
end
else
begin
if c=')' then
begin
Dec(Fghpd);//括号匹配
abds := CopyRight(abds,2);
if Fghpd < 0 then
Raise Exception.Create('括号不配对');
Result := lpp.CalcValue;
//返回括号中的子项值,进行下一步计算
lpp.Free;
Exit;
end
else
begin
if c='(' then
Inc(Fghpd);//做括号层数标识
lpp.PushValue(BdsYz(abds));//取下一项的值。
end;
end;
end;
if Fghpd< >0 then
Raise Exception.Create('括号不配对');
Result := lpp.CalcValue;//返回最终运算值
lpp.Free;
end;

function TBdsProc.BdsYZ(var abds: String): ValKind;
begin
if abds< >'' then
begin
if abds[1]='(' then
begin
abds := CopyRight(abds,2);
Result := BdsSs(abds);//递归调用,求括号中的值
end
else
Result := BdsItm(abds);{读一个数据项,
如果包括函数定义,可以在该方法中定义接口或连接}
end;
end;
若表达式要支持函数功能,只要定义系统支持的函数,
在取行表达式因子BdsItm中加入连接函数定义即可,如下:
…..
else
if (c< ='Z') and (c >='A') then
begin
bhs := 取出函数名及参数
Result := … //调用函数处理 (函数定义)
abds := 下一项
end
….
< 2 > 数据计算:主要包PushValue,PushFh,
Calccur分别完成存入数、符号、中间计算
procedure TPUSHPOP.PushValue(avalue: CDIKind);
begin
if fisfh=True then
Raise Exception.Create('缺少运算符');
if fisnot then
//进行非运算
begin
if avalue.id then
Raise Exception.Create('浮点数不能做非运算');
avalue.ival := not avalue.ival;
fisnot := False;
end;
if IsLgFh(ffh[flevel]) then
//运行逻辑运算
begin
value[flevel] := Calclg(value[flevel],
avalue,ffh[flevel]);
//与当前值做逻辑运算
end
else
begin
Inc(flevel);//存数位置指针加1
value[flevel] := avalue;//存入值
if flevel >2 then
//数据个数达到4,进行中间运算
Calccur;
end;
fisfh := True;//输入符号可见
end;

procedure TPUSHPOP.PushFh(afh: Char);
begin
if (fisfh=false) then
//非运算是一级
begin
if (afh='!') and (not fisnot) then
//标识非运算
begin
fisnot := True;
Exit;
end
else
Raise Exception.Create('运算符重复');
End
else
begin
ffh[flevel] := afh;//存入运算符
fisfh := False;
输入值可见
end;
end;

procedure TPUSHPOP.Calccur;
begin
if IsCcFh(ffh[1]) then
//二级运算符
begin
value[1] := Calcsj(value[1],value[2],ffh[1]);
//计算2和3项的值
ffh[1] := ffh[2];//后序运符和值前移
value[2] := value[3];
end
else
//一级运算符
begin
value[0] := Calcsj(value[0],value[1],ffh[0]);
//计算1和2项的值
value[1] := value[2];{ 后序运符和值前移,
2和3项的值不计算是为了保持第一个运算符为一级运算符}
value[2] := value[3];
ffh[0] := ffh[1];
ffh[1] := ffh[2];
end;
Dec(flevel);//存数位置指针减1
end;

以上来扑克hubdog的,看一下有没有help,我没时间去研究,如果你弄好了通知我一下,最好
全发给我,呵呵...
 
上面的程序讲的都是一些容易处理的数学两目运算,我要去考研了
送分了!!!
 
后退
顶部