php数学公式解析 看看架构师是如何架构设计的

最近工作中需要用到公式解析工具,我就在网上搜索到了一段代码,

public class Expression
{
private StackSimple<int> theStack;
public StringBuilder suffix = new StringBuilder();
/**
* 检查该字符是否为括号
*/
private bool isParen(char c)
{
if (c == '{' || c == '[' || c == '(' || c == ')' || c == ']' || c == '}')
return true;
else
return false;
}
/**
* 检查是否为左括号
*/
private bool isLeftParen(char c)
{
if (c == '{' || c == '[' || c == '(')
return true;
else
return false;
}
/**
* 判断指定的操作符是否为 '+' 或者 '-'
*/
private bool isAddOrSub(int oper)
{
return oper == '+' || oper == '-';
}
/**
* 检查是否为数字
*/
private bool isNumber(int ch)
{
return ch >= 48 && ch <= 57;
}
/**
* 检查表达式是否合法
*/
private bool checkExp(String exp)
{
char[] ch = exp.ToCharArray();
StackSimple<int> theStackXInt = new StackSimple<int>(exp.Length / 2);
/**
* 遍历表达式,只处理括号部分
*/
for (int i = 0; i < ch.Length; i++)
{
char c = ch[i];
if (isParen(c))
{
/**
* 左括号进栈
*/
if (isLeftParen(c))
{
theStackXInt.Push(c);
}
else
{
if (theStackXInt.IsEmpty())
{
return false;
}
int left = theStackXInt.Pop();
switch (c)
{
case '}':
if (left != '{')
return false;
break;
case ']':
if (left != '[')
return false;
break;
case ')':
if (left != '(')
return false;
break;
}
}
}
}
if (theStackXInt.IsEmpty())
{
return true;
}
else
{
return false;
}
}
/**
* 根据当前操作符处理之前在栈中的操作符,当前操作符暂不处理,放入栈中
*/
private void processOper(char oper)
{
while (!theStack.IsEmpty())
{
int currTop = theStack.Pop();
/**
* 如果是左括号,则不予处理,将括号返回进栈
*
* 若得到的操作符,则进一步判断
*/
if (currTop == '(')
{
theStack.Push(currTop);
break;
}
else
{
if (this.isAddOrSub(currTop))
{
if (this.isAddOrSub(oper))
{
this.suffix.Append((char)currTop);
}
else
{
this.theStack.Push(currTop);
break;
}
}
else
{
this.suffix.Append((char)currTop);
break;
}
}
}
/**
* 当前操作符进栈
*/
theStack.Push(oper);
}
/**
* 处理括弧.
*
* 如果为左括弧,直接进栈;
*
* 否则为右括弧,现将这一对括号中的操作符优先处理完
*/
private void processParen(char paren)
{
if (this.isLeftParen(paren))
{
this.theStack.Push(paren);
}
else
{
while (!theStack.IsEmpty())
{
int chx = theStack.Pop();
if (chx == '(')
break;
else
suffix.Append((char)chx);
}
}
}
/**
* 将正确的中缀表达式转为后缀表达式
*/
private void doTrans(String exp)
{
theStack = new StackSimple<int>(exp.Length);
if (this.checkExp(exp))
{
char[] ch = exp.ToCharArray();
for (int i = 0; i < ch.Length; i++)
{
char c = ch[i];
switch (c)
{
case '+':
case '-':
case '*':
case '/':
this.processOper(c);
break;
case '(':
case ')':
this.processParen(c);
break;
default:
this.suffix.Append(c);
break;
}
}
while (!this.theStack.IsEmpty())
{
this.suffix.Append((char)this.theStack.Pop());
}
}
}
/**
* 计算表达式
*/
public int clac(String exp)
{
this.doTrans(exp);
String suff = this.suffix.ToString();
StackSimple<int> stack = new StackSimple<int>(suff.Length);
if (suff == null || suff.Length == 0)
{
}
else
{
char[] ch = suff.ToCharArray();
for (int i = 0; i < ch.Length; i++)
{
char c = ch[i];
if (this.isNumber(c))
{
stack.Push(c - 48);
}
else
{
int operand2 = stack.Pop();
int operand1 = stack.Pop();
switch (c)
{
case '+':
stack.Push(operand1 + operand2);
break;
case '-':
stack.Push(operand1 - operand2);
break;
case '*':
stack.Push(operand1 * operand2);
break;
case '/':
stack.Push(operand1 / operand2);
break;
}
}
}
}
return stack.Peep();
}
}

先解释下这个类的执行过程:遍历一个字符串公式,如果是数字,放到栈里,非则放到临时栈里,如果发现+或者-,就把临时栈里的top取出放入栈里,如果发现的是非+和-(因为+和-优先级太低了。。。)放进临时栈里,如果发现的是括号,则把临时栈top进栈里,直到发现左括号为止。。。。。。。。。。。。

因为我用的是php,所以我把这段代码翻译成php的,这段代码看上去就像是一坨屎啊,下面我要把这坨屎重构成香饽饽........

第一步:首先要弄明白公式解析是啥东西,怎么个流程,包含哪些步骤,这是一个工具,用来解析公式字符串的,没错,解析公式字符串,那么公式字符串呢,我们要先判断公式是否合法,然后呢,既然合法了,那就解析啊,解析成程序可以识别的数据(栈),再燃后呢,既然都能识别了,那就是计算啊.....

第二部步:现在开发思考,怎么设计类结构了,根据上一步的分析,大体上有3个类,Expression(主类)、CheckExpression(检查是否合法,并返回可识别栈)、operateclass(计算类),这个代码就出来了。

现在针对上面的类进行重构:

1、先看主类,因为这是个工具,一个急救箱能救无数人,而不是每一个人配一个急救箱,是唯一对象,所以要设计成单类模式。

2、CheckExpression和operatecass这两个类是为主类服务的,脱离了主类就没有意义了,所以这两个类和主类是聚合关系。

3、php的函数映射就是屌,扩展起来很方便,直接上代码:

class OperateClass
{
public $arr = array(
"+" => "add",
"-" => "sub",
);
public function OpreateArr($a, $b)
{
$func = new $operate();
return $func($a,$b);
}
private function add($a,$b)
{
return $a + $b;
}
private function sub($a,$b)
{
return $a + $b;
}
}
有兴趣的同学可以把参数改成数组,面对多个参数的数学公式时,容易扩展。

4、个人的一个习惯,写类的时候总喜欢先写个cleanup()方法。。。强迫症

5、好多地方都要加上异常判断,比如除数是0等等。

6、checkExp(String exp)和doTrans(exp) 这两个函数里都有for循环,这两个函数可以整合成一个函数,提高性能。

大体上就先写到这里吧,架构师干的不就是架构框架、重构代码、优化提高性能嘛,咱们这个公式解析类,麻雀虽小,五脏俱全。

转载于:https://www.cnblogs.com/dashu122/p/5924936.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值