后缀表达式

    我经常不厌其烦地向开发人员强调数据结构的重要性,也经常和团队分享一些常见算法。也许是大家写惯了业务代码,往往对数据结构的知识并不太在意。可不是嘛,只需要玩转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

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

原文地址:https://www.cnblogs.com/bigmonkey/p/7289351.html