• 后缀表达式


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

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

  • 相关阅读:
    关于C#静态构造函数的几点说明
    《JavaScript高级程序设计》读书笔记之一:几个与原始类型等价的引用类型的常用方法和属性
    Ajax 简介
    如何优化JavaScript脚本的性能
    摆脱混沌,建立个人能力体系——病症四起【from csdn】
    JMX理解与实例
    苹果电脑不为人所知的第三个创始人
    一些AS3中常用到的公式
    JavaScript 随笔
    解决IE6 JSONP无响应的问题。
  • 原文地址:https://www.cnblogs.com/bigmonkey/p/7289351.html
Copyright © 2020-2023  润新知