首页 > 学院 > 开发设计 > 正文

文本逻辑表达式代码化

2019-11-06 09:28:34
字体:
来源:转载
供稿:网友

问题引出

笔者目前在机票行业,前几天有同事聊到一个这样的问题。某个报价是否可对用户可见,不同的航司可能要求不一样,比如国航会要求时间满足一定条件,南航可能会要求日期并且仓位满足一定条件,而东航可能会要求日期或者仓位满足要求,甚至要求会更复杂,而航司也会很多,也许还会更改判定条件,那么遇到这种情况要如何处理呢?

思路

由于要保留可变性,所以需要做到配置的,而可配置的一定都是字符串,那么我们要做的就是将文字描述的逻辑关系转化为相应的代码描述。 而要实现这两点,想到了以下两点. 1. 学编译原理的时候,提到的四则运算时的中辍表达式转后辍表达式(也叫波兰式与逆波兰式) 2. 利于Guavacom.google.common.base.PRedicates将多个com.google.common.base.Predicate转化为一个最终的Predicate,利用predicate.apply()就可以判定结果是否满足。

代码

直接上代码,代码中没有直接实现判定的逻辑,在实际中可以自行实现。 支持的逻辑表达有: 与:用&表示;或:用|表示;并且支持括号 参数(或者叫判定条件有):年龄,性别,地址,学校,学科。 这个只是个举例,具体的如何判定没有实现,这个判定条件是随机写的,没有明确的意义。

一个标志接口

package com.personal;/** * Created by Administrator on 2017/2/23. * 定义了一个标志接口,因为中间使用的时候,会往List中放相应的判定条件及运算符 */public interface Meta{}

逻辑关系运算符

package com.personal;import com.google.common.base.Predicate;import com.google.common.base.Predicates;/** * Created by Administrator on 2017/2/23. * 定义的逻辑运算符 */public enum Operator implements Meta{ AND { boolean is ( String operator ) { return "&".equals( operator ); } public < T > Predicate< T > combine ( Predicate< T > predicate1, Predicate< T > predicate2 ) { return Predicates.and(predicate1,predicate2); } }, OR { boolean is ( String operator ) { return "|".equals( operator ); } public < T > Predicate< T > combine ( Predicate< T > predicate1, Predicate< T > predicate2 ) { return Predicates.or(predicate1,predicate2); } }, LEFT_BRACKET { boolean is ( String operator ) { return "(".equals( operator ); } }, RIGHT_BRACKET { boolean is ( String operator ) { return ")".equals( operator ); } }; // 用于判定输入的字符串是否是一个运算符 abstract boolean is (String operator); // 合并多个 Predicate public <T> Predicate<T> combine(Predicate<T> predicate1,Predicate<T> predicate2){ return null; } public static Operator match ( String operator){ for ( Operator label : values() ) { if ( label.is( operator ) ){ return label; } } return null; }}

判定条件

package com.personal;import com.google.common.base.Predicate;/** * Created by Administrator on 2017/2/23. * 逻辑表达式,对应不同的具体的限制条件,不同的限制条件可以生成不同的Predicate,这里提供了默认的apply()方法 */public enum Data implements Meta{ SEX { public <T> Predicate< T > createPredicate () { return new Predicate< T >() { public boolean apply ( T input ) { return false; } }; } }, SCHOOL { public <T> Predicate< T > createPredicate () { return new Predicate< T >() { public boolean apply ( T input ) { return false; } }; } }, SUBJECT { public <T> Predicate< T > createPredicate () { return new Predicate< T >() { public boolean apply ( T input ) { return false; } }; } }, AGE { public <T> Predicate< T > createPredicate () { return new Predicate< T >() { public boolean apply ( T input ) { return false; } }; } }; public abstract <T> Predicate< T > createPredicate ();

具体的转化方法

package com.personal;import com.google.common.base.Predicate;import com.google.common.collect.Lists;import java.util.ArrayList;import java.util.List;import java.util.Stack;import static com.google.common.base.Preconditions.checkArgument;/** * Created by Administrator on 2017/2/23. * 将输入转化为Predicate,如果在应用中,将source字符串做成配置即可。 */public class Factory { public static void main ( String[] args ) { String source = "SEX&(SCHOOL|SUBJECT)|AGE"; Predicate<Object> predicate = buildPredicate(source); // 这里的输出是没有意义的,验证的时候可以在这里打个断点,看下predicate的组成,验证下是否正确。 System.out.println(predicate); } /** * 分成三步, * 1. String串转化为我们自己定义的变量,转化为相应的逻辑表达式 * 2. 将中辍逻辑表达式转化为后缀逻辑表达式 * 3. 将后缀逻辑表达式转化为最终的结果 * @param source 原输入 * @return */ private static Predicate< Object > buildPredicate ( String source ) { List<Meta> metas = transferStringToMeta(source); List<Meta> postFixExpress = transferToPostExpress(metas); return transferToPredicate( postFixExpress); } private static List< Meta > transferStringToMeta ( String source ) { List<Meta> labels = new ArrayList< Meta >( 10 ); StringBuilder sb = new StringBuilder( 10 ); for ( int i = 0; i < source.length(); i++ ) { Operator temp = Operator.match( source.substring( i,i+1 ) ); if ( temp == null){ sb.append( source.charAt( i ) ); }else { if (sb.length() > 0 ){ labels.add( ( Data.valueOf( sb.toString() ))); } labels.add( temp ); sb = new StringBuilder( 10 ); } } labels.add( ( Data.valueOf( sb.toString() ))); return labels; } /** * 转化为后缀表达式 * 依次读原来的表达式, * 如果是参数,则直接输出 * 如果是运算符 * 如果是左括号,则直接放入栈 * 如果是右括号,则将栈中的内容取出来,直到左括号,左括号不输出 * 如果是其它,则从栈中输出优先级不小于此运算符的运算符,并且将当前的运算符再压入栈 * (注意括号,只有遇到右括号的时候才会把栈中的左括号出栈) * @param metas 中辍逻辑表达式 * @return */ private static List< Meta > transferToPostExpress ( List< Meta > metas ) { List<Meta> result = Lists.newArrayList(); Stack<Meta> metaStack = new Stack< Meta >(); for ( Meta meta : metas ) { if ( meta instanceof Data ){ result.add( meta ); }else if ( meta instanceof Operator ){ List<Meta> temp = processLabel(meta,metaStack); if ( temp != null && temp.size() > 0 ) { result.addAll( temp ); } }else { throw new RuntimeException( "error meta type" ); } } while(metaStack.size() > 0){ result.add( metaStack.pop()); } return result; } /** * 后缀表达式,转化为最终的结果 * 如果遇到参数,直接入栈 * 如果遇到运算符,则将栈中的两个参数取出,计算后,再次入栈,直到所有的输入全部处理完 * * 最终,栈中应该只流一个参数。 * @param metas * @return */ private static Predicate< Object > transferToPredicate ( List< Meta > metas ) { checkArgument(metas != null && metas.size() > 0,"express cannot be null"); Stack<Predicate< Object >> metaStack = new Stack< Predicate< Object > >(); for ( Meta meta : metas ) { if ( meta instanceof Operator ){ Predicate<Object> predicate1 = metaStack.pop(); Predicate<Object> predicate2 = metaStack.pop(); Predicate<Object> temp = ( ( Operator ) meta ).combine( predicate1,predicate2 ); metaStack.push( temp ); }else if ( meta instanceof Data ){ metaStack.push( ( ( Data ) meta ).createPredicate() ); }else { throw new RuntimeException( "unrecognized label" ); } } if ( metaStack.size() > 1 ){ throw new RuntimeException( "surplus meta" ); } return metaStack.pop(); } private static List< Meta > processLabel ( Meta meta, Stack< Meta > metaStack ) { if ( Operator. LEFT_BRACKET == meta){ metaStack.push( meta ); return null; } if ( Operator.RIGHT_BRACKET == meta){ List<Meta> result = Lists.newArrayList(); while(true){ Meta temp = metaStack.pop(); if ( temp != Operator.LEFT_BRACKET ){ result.add( temp ); }else { break; } } return result; } if ( Operator.AND == meta ){ metaStack.push( meta ); } // 取出优先级等于高于此优先级的运算符, 并把自己的放进去。 if ( Operator.OR == meta ){ List<Meta> result = Lists.newArrayList(); while ( metaStack.size() > 0 ){ Meta temp = metaStack.pop(); if ( temp == Operator.OR || temp == Operator.AND ){ result.add( temp ); }else { metaStack.push( temp ); break; } } metaStack.push( meta ); return result; } return null; }}

结论

采用这种方案,可以自行配置不同的判定条件,当需要将增一种判定条件时,只需要在Data中新增加一种枚举类型即可,方便扩展,也易于理解。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表