后缀表达式

  我经常不厌其烦地向开发人员强调数据结构的重要性,也经常和团队分享一些常见算法。也许是大家写惯了业务代码,往往对数据结构的知识并不太在意。可不是嘛,只需要玩转QUID就能解决90%以上的问题,还费脑细胞学习基础知识干什么?当然,所有人都会回答“基础知识很重要,数据结构很重要”。然而,当你发现工作几年的开发人员甚至不知道Array和List的区别时,也许会觉得他们当初是在说谎。这是真的,我发现大部分人不知道还有个链表叫“循环链表”。

  下面讲述的是多年前的故事。

  表格曾是我们在JSP页面常用的布局方式,碰到合并单元格时会不停的使用collspan和rowspan。为了简化操作,我规划了一个布局管理器,用一个字符串代替表格布局。大概长这个样子: layout="3;C2(bop1, bop2),C3(bop3, bop4)" ,layout是自定义标签属性,bop对应布局中的一个细粒度组件,3表示默认3列布局,C2和C3分别表示合并2个和3个单元格。连起来的意思就是,当前容器默认3列布局,其中bop1和bop2需要占两列,bop3和bop4需要占3列,其余组件占1列。还可以进行行合并:layout="3;R2(C2(bop1, bop2)),C3(bop3, bop4)",表示bop1, bop2合并两列后再合并两行。当然,你可以写的更加随意,只要有意义即可,如:layout="3;R2(C2(bop1)),R2(C2(bop2)),C3(bop3, bop4)",layout="4;C2(bop1),C2(bop2),C3(bop3)"。

  当时我把这个任务分配给一个工作了4年左右开发人员,并告诉他去看看后缀表达式,并顺便看看解析器模式。

  两天后我得到了任务完成的答复,然后开始review。

  这下好玩了,布局管理器变成了这样:layout="3;C2:bop1,bop2;C3:bop3;bop4"。似乎没那么坏,至少还能工作,但看起来没那么直观了,似乎也不能更加随意。私自更改设计的原因很简单——这样可以更容易使用split,哈,貌似没有理解如何使用数据结构。

  后来使用后缀表达式重写了实现。这实际上是栈结构的典型应用,遍历字符串,根据各种符号为节点,不断入栈出栈。具体过程如下:

  1. 刨除前面的默认列数

  2. cellMap存储最终解析结果,key对应bop名称,value对应布局,即合并几行几列;stack是表达式栈,存储R1,C1,R2,C2这类xspan表达式;atom是字符串,代表xspan表达式RX,CX或bop

  3. 遍历布局管理器字符串

  3.1. 遇到数字或字母直接并入atom

  3.2  遇到左括号,将atom压入stack,清空atom

    3.3  遇到逗号, 如果stack不为空, 将stack中的所有xspan表达式代表的含义赋予bop, 将bop压入cellMap,清空atom; 如果stack为空, 清空atom

  3.4  遇到右括号, 将stack中的所有xspan表达式代表的含义赋予bop, 将bop压入cellMap, 从stack弹出一项.

  4.布局管理器字符串遍历结束,cellMap是最终结果

  以R2(C2(bop1, bop2)),C3(bop3, bop4)为例:

 1.遍历字符串,atom="R2"

  2.遇到"(",stack.push(atom),atom="",  stack栈顶元素为"R2"

  3.继续遍历字符串,atom="C2"

  4.遇到"(",stack.push(atom),atom="",  stack栈顶元素为"C2",第二个元素为"R2"

  5.继续遍历字符串,atom="bop1"

  6.遇到",",cellMap.put("bop1", "C2,R2"),atom=""

  7.继续遍历字符串,atom="bop2"

  8.遇到")",cellMap.put("bop2", Cell.C2),stack.pop(), stack中C2出栈,R2升到栈顶

  9.遇到")",cellMap.put("bop2", Cell.R2),stack.pop(), stack中R2出栈,stack为空

  10.遇到",",此时stack为空,所以atom=""

  11.继续遍历字符串,atom="C3"

  12.遇到"(",stack.push(atom),atom="",  stack栈顶元素为"C3"

  13.继续遍历字符串,atom="bop3"

  14.遇到",",cellMap.put("bop3", "C3"),atom=""

  15.继续遍历字符串,atom="bop4"

  16.遇到")",cellMap.put("bop4", "C3"),stack.pop(), stack中C3出栈,stack为空

  17.遍历结束,最终得到cellMap

代码示例:

  1 /**
  2  *
  3  * 布局管理器表达式解释器
  4  */
  5 public class Interpreter {
  6
  7     /*
  8      * 布局管理器表达式
  9      * 布局管理器表达式示例:
 10      *     1.  3;C2(bop1, bop2),C3(bop3, bop4);
 11      *     默认3列,bop1,bop2合并2列,bop3,bop4合并3列;
 12      *  2.  4;R2(C2(bop1, bop2)),C3(bop3, bop4)
 13      *  默认4列,bop1,bop2合并2列2行,bop3,bop4合并3列;
 14      *  3.  粗粒度组件布局管理器表达式类似: C2(boid1, boid2).
 15      */
 16     private String layoutStr;
 17     private int columns;    //布局的列数
 18     /*
 19      * key : vcSign, value : formCell
 20      * vcSign:如果是细粒度组件,vcSign表示细粒度组件标签的bind属性;
 21      * 如果是粗粒度组件,vcSign表示粗粒度组件标签的id属性。
 22      */
 23     private Map<String, Cell> cellMap = new HashMap<String, Cell>();
 24
 25     private final String EXPRESSION_SPLIT = ";";
 26     private final char FC_SPLIT = ‘,‘;
 27     private final char LEFT_BRACKET = ‘(‘;
 28     private final char RIGHT_BRACKET = ‘)‘;
 29
 30     /**
 31      * @param layoutStr      布局管理器表达式
 32      * @param defColumns     默认列数
 33      */
 34     public Interpreter(String layoutStr, int defColumns) {
 35         this.layoutStr = StringUtils.removeAllSpace(layoutStr);
 36         this.columns = defColumns;
 37     }
 38
 39     /**
 40      * 解析布局管理器表达式.
 41      */
 42     public void interpret() {
 43         if(StringUtils.isEmpty(getLayoutStr()))
 44             return;
 45
 46         String[] layoutSplit = StringUtils.split(getLayoutStr(), EXPRESSION_SPLIT);
 47         if(StringUtils.isEmpty(layoutSplit))
 48             return;
 49
 50         interpertColumns(layoutSplit[0]);
 51         for(String str : layoutSplit) {
 52             if(StringUtils.isEmpty(str) || MatcherUtil.isNumber(str))
 53                 continue;
 54
 55             interpertFormCell(str);
 56         }
 57     }
 58
 59     /**
 60      * 解析默认列数
 61      * @param str    布局管理器划分的总列数
 62      */
 63     private void interpertColumns(String str) {
 64         Integer columns = StringUtils.convertToInteger(str);
 65         if(columns != null && columns > 0)
 66             setColumns(columns);
 67     }
 68
 69     /**
 70      * 构造每个bop的布局样式
 71      * R2(C2(bop1, bop2)) 或  R2(bop3, C2(bop1, bop2))
 72      * <li>1. 遍历表达式, 解析子元素;
 73      * <li>2. 遇到左括号,将R2, C2 表达式压入表达式栈顶;
 74      * <li>3. 遇到逗号, 如果栈不为空, 将栈中的所有表达式代表的含义赋予bop, 将bop压入cellMap; 如果栈为空, 直接略过;
 75      * <li>4. 遇到右括号, 将栈中的所有表达式代表的含义赋予bop, 将bop压入cellMap, 从表达式栈顶弹出一项.
 76      * @param str
 77      */
 78     private void interpertFormCell(String str) {
 79         //表达式栈, 存储 R1, C1, R2, C2等合并单元格的表达式
 80         Stack<String> stack = new Stack<String>();
 81
 82         //表达式的元素
 83         String atom = "";
 84         //遍历表达式, 解析子元素
 85         for(int i = 0, length = str.length(); i < length; i++) {
 86             char ch = str.charAt(i);
 87             //遇到左括号,将R2, C2 表达式压入表达式栈顶
 88             if(LEFT_BRACKET == ch) {
 89                 stack.push(atom);
 90                 atom = "";
 91             }
 92             //遇到逗号, 如果栈不为空, 将栈中的所有表达式代表的含义赋予bop, 将bop压入cellMap;
 93             //如果栈为空, 直接略过;
 94             else if(FC_SPLIT == ch) {
 95                 if(ContainerUtil.isNotNull(stack))
 96                     formatCell(atom, stack);
 97                 atom = "";
 98             }
 99             //遇到右括号, 将栈中的所有表达式代表的含义赋予bop, 将bop压入cellMap, 从表达式栈顶弹出一项
100             else if(RIGHT_BRACKET == ch) {
101                 if(StringUtils.isNotEmpty(atom))
102                     formatCell(atom, stack);
103                 stack.pop();105             }
106             else {
107                 atom += ch;
108             }
109         }
110     }
111
112     /**
113      * 将bop压入cellMap
114      * @param vcSign
115      * @param stack 表达式栈
116      */
117     private void formatCell(String vcSign, Stack<String> stack) {
118         Cell cell = getCell(vcSign);
119
120         Iterator<String> itr = stack.iterator();
121         while(itr.hasNext()) {
122             String expression = itr.next();
123             if(isCELLSPAN(expression))
124                 cell.setCollSpan(getCallSpan(expression));
125             else if(isROWSPAN(expression))
126                 cell.setRowSpan(getRowSpan(expression));
127         }
128
129         cellMap.put(vcSign, cell);
130     }
131
132     private int getCallSpan(String expression) {
133         int collSpan = Integer.parseInt(expression.toUpperCase().replace("C", ""));
134         return collSpan > getColumns() ? getColumns() : collSpan;
135     }
136
137     private int getRowSpan(String expression) {
138         return Integer.parseInt(expression.toUpperCase().replace("R", ""));
139     }
140
141     private Cell getCell(String atom) {
142         Cell cell = cellMap.get(atom);
143         return cell == null ? new Cell() : cell;
144     }
145
146     private boolean isCELLSPAN(String str) {
147         return MatcherUtil.isAllMatch(str, "^[C|c]-?\\d+$");
148     }
149
150     private boolean isROWSPAN(String str) {
151         return MatcherUtil.isAllMatch(str, "^[R|r]-?\\d+$");
152     }
153
154     public int getColumns() {
155         return this.columns;
156     }
157
158     public void setColumns(int columns) {
159         this.columns = columns;
160     }
161
162     public String getLayoutStr() {
163         return layoutStr;
164     }
165
166     public void setLayoutStr(String layoutStr) {
167         this.layoutStr = layoutStr;
168     }
169
170     public Map<String, Cell> getCellMap() {
171         return cellMap;
172     }
173
174     public void setCellMap(Map<String, Cell> cellMap) {
175         this.cellMap = cellMap;
176     }


作者:我是8位的

出处:http://www.cnblogs.com/bigmonkey

本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途!

时间: 2024-12-24 20:07:54

后缀表达式的相关文章

算法学习 - 表达树的建立(后缀表达式法),树的先序遍历,中序遍历,后序遍历

表达树就是根据后缀表达式来建立一个二叉树. 这个二叉树的每个叶子节点就是数,真祖先都是操作符. 通过栈来建立的,所以这里也会有很多栈的操作. 树的先序遍历,中序遍历,后序遍历的概念我就不讲了,不会的自行百度,不然也看不懂我的代码. 下面是代码: // // main.cpp // expressionTree // // Created by Alps on 14-7-29. // Copyright (c) 2014年 chen. All rights reserved. // #includ

四则运算使用栈和后缀表达式

我们在一般的四则运算都是中缀表达式. 别问我什么是中缀表达式. 我就知道符号两边是数字.也就是符号在中间. 1+3 什么是后缀表达式呢? 13+ 符号在后面. 那么就有人奇怪了,为什么我要使用后缀表达式呢? 问题就是运算的时候,在编程实现的时候,后缀表达式比中缀表达式好用呗. 没事,不理解那里好用没事,我们先把中缀表达式转换成后缀表达式然后实际看看就知道了. 从左到右依次读取 运算数,输出 左括号,入栈 右括号,将栈顶的运算符并输出,直到遇到左括号 运算符 { 若优先级大于栈顶运算符,入栈 若优

中缀表达式与前、后缀表达式转化简单的技巧[转]

35,15,+,80,70,-,*,20,/ //后缀表达方式 (((35+15)*(80-70))/20)=25 //中缀表达方式 /,*,+,35,15,-,80,70, 20 //前缀表达方式 人的思维方式很容易固定~~!正如习惯拉10进制.就对2,3,4,8,16等进制不知所措一样~~! 人们习惯的运算方式是中缀表达式.而碰到前缀,后缀方式..迷茫其实仅仅是一种表达式子的方式而已(不被你习惯的方式) 我这里教你一种也许你老师都没跟你讲的简单转换方式 一个中缀式到其他式子的转换方法 这里我

数据结构Java实现06----中缀表达式转换为后缀表达式

数据结构Java实现06----中缀表达式转换为后缀表达式 本文主要内容: 表达式的三种形式 中缀表达式与后缀表达式转换算法 一.表达式的三种形式: 中缀表达式:运算符放在两个运算对象中间,如:(2+1)*3.我们从小做数学题时,一直使用的就是中缀表达式. 后缀表达式:不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则),如:2 1 + 3 *.又比如3+(6-4/2)*5=23的后缀表达式为:3642/-5*+# (#符号为结束符

中缀表达式转为后缀表达式

** * 中缀表达式转后缀表达式 * * 作用:将一长串计算表达式转换为计算机易于操作的字符序列,用于计算器的设计 *  * 参与转换运算符 * +-/*()^% * * * 使用StringBuilder来保存转换出的后缀表达式 * 使用栈来操作运算符 * * * 转换原则 * 1.上述字符中()没有优先级值,+-优先级值为1,/*%优先级值为2,^优先级值为3 * 2.对于一个待计算的表达式,从左向右逐个检查每个字符 * 3.遇到数字,直接append到StringBuilder * 4.遇

中缀表达式转后缀表达式的方法:

1.遇到操作数:直接输出(添加到后缀表达式中)2.栈为空时,遇到运算符,直接入栈3.遇到左括号:将其入栈4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出.5.遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈6.最终将栈中的元素依次出栈,输出.例如a+b*c+(d*e+f)*g ----> abc*+de*f+g*+ 遇到a:直接输出:后缀表达式:a堆栈:空 遇到+:堆栈:空,所以+入栈后缀表达式:a堆栈:+遇到b: 直

中缀表达式转后缀表达式

写之前应该介绍一个厉害的波兰数学家--Jan Lukasiewicz(鲁卡谢维奇),他想到了一种不需要括号的后缀表达法,我们为了纪念他,把它称为逆波兰(Reverse Polish Notation,RPN)表示. 我们把平时所用的标准四则运算表达式,如:9+(3-1)×3+10/2,称为中缀表达式. 把9 3 1 - 3 * + 10 2 / +(此处由上一行的中缀表达式例子转化而来) 这种不需要括号表示的形式叫做后缀表达式,也是计算机非常喜欢的计算式. 转换规则: 从左到右遍历中缀表达式的每

数据结构笔试:前缀表达式|后缀表达式

这里我给出一个中缀表达式:a+b*c-(d+e) 第一步:按照运算符的优先级对所有的运算单位加括号:式子变成了:((a+(b*c))-(d+e)) 第二步:转换前缀与后缀表达式 前缀:把运算符号移动到对应的括号前面 则变成了:-( +(a *(bc)) +(de)) 把括号去掉:-+a*bc+de 前缀式子出现 后缀:把运算符号移动到对应的括号后面 则变成了:((a(bc)* )+ (de)+ )- 把括号去掉:abc*+de+- 后缀式子出现 so easy~

中缀表达式转化为后缀表达式

后缀表达式 逆波兰记法中,操作符置于操作数的后面.例如表达"三加四"时,写作"3 4 +",而不是"3 + 4".如果有多个操作符,操作符置于第二个操作数的后面,所以常规中缀记法的"3 - 4 + 5"在逆波兰记法中写作"3 4 - 5 +":先3减去4,再加上5.使用逆波兰记法的一个好处是不需要使用括号.例如中缀记法中"3 - 4 * 5"与"(3 - 4)*5"不