Interpreter mode -- the formula of blind date

Posted by Volitics on Mon, 03 Jan 2022 00:37:03 +0100

Introduction

Xiaomei was recently urged by her family to have a blind date. Her relatives introduced several boys. Xiaomei was not satisfied. Xiaomei's requirements are high: at least have a room? The annual income can't be less than 200000, can it? The minimum height is 175cm, right? You can't be too old, can't you be over 30? Education? Do you want to start as an undergraduate?

what? You think I'm asking too much? Well, if your family is rich, other conditions can also be relaxed!

Xiaomei deserves to be a procedural yuan. She lists her blind date conditions into a formula:

House > = 1 & & annual income > = 20 & & height > = 175 & & age < 30 & & Education > = 4 | property > = 1000

Xiaomei gave this formula to the matchmaker and told the boy who must meet this formula to arrange a blind date. When the matchmaker looks at the direct force, how can there be a formula? Girl, I'm just a matchmaker and haven't studied mathematics. Don't you embarrass me?

Xiaomei said: don't worry. Only by applying the formula can you ensure the correct result. Listen to me carefully. This has to start with the interpreter mode

Matchmaker: I don't understand

Xiaomei: No, you understand

Interpreter mode

Interpreter pattern: define its syntax (or grammar) representation for a language, and define an interpreter to process the syntax. (Interpreter pattern is used to defines a grammatical representation for a language and provides an interpreter to deal with this grammar.)

In the vernacular, we can define some symbols ourselves, and then we set rules for these symbols, and the power of interpretation is in our own hands.

For example, an expression "1#2! 3 ", we can define" # "to mean" + "sign,"! " How to explain the "-" has the final say.

The class diagram of interpreter mode is as follows:

I use the expression a+b-c to apply:

  • AbstractExpression: Abstract interpreter. The specific interpretation task is completed by each implementation class. The specific interpreter is completed by TerminalExpression and NonterminalExpression respectively.
  • TerminalExpression: Terminator expression, which corresponds to "a", "b", "c".
  • NonterminalExpression: non terminator expression, which corresponds to "+", "-" symbols.
  • Context: some global information outside the interpreter. In the following example, it is the Map in the client.

For the expression of blind date, the following structure is adopted:

With that, Xiaomei threw out a piece of code:

Here, we assume that "> =", the priority of "<" symbol is higher than "& &" and "|", and the priority of "& &" is higher than "|".

Expression interface class:

/**
 * Expression interface
 */
public interface Expression {
    /**
     * Interpretation expression
     * @param states
     * @return
     */
    boolean interpret(Map<String, Integer> states);
}

Expression class greater than or equal to:

/**
 * Greater than or equal to expression
 */
public class GreaterOrEqualExpression implements Expression {
    private String key;
    private int value;

    public GreaterOrEqualExpression(String key, int value) {
        this.key = key;
        this.value = value;
    }

    public GreaterOrEqualExpression(String strExpression) {
        String[] elements = strExpression.trim().split("\\s+");
        if(elements.length != 3 || !elements[1].trim().equals(">=")) {
            throw new RuntimeException("Expression is invalid: " + strExpression);
        }
        this.key = elements[0].trim();
        this.value = Integer.parseInt(elements[2].trim());
    }

    /**
     * Explain expressions greater than or equal to
     * @param states
     * @return
     */
    @Override
    public boolean interpret(Map<String, Integer> states) {
        if(!states.containsKey(key)) {
            return false;
        }
        int stateValue = states.get(key);
        return stateValue >= value;
    }
}

Less than expression class:

/**
 * Less than expression
 */
public class LessExpression implements Expression {
    private String key;
    private int value;

    public LessExpression(String key, int value) {
        this.key = key;
        this.value = value;
    }

    public LessExpression(String strExpression) {
        String[] elements = strExpression.trim().split("\\s+");
        if(elements.length != 3 || !elements[1].trim().equals("<")) {
            throw new RuntimeException("Expression is invalid: " + strExpression);
        }
        this.key = elements[0].trim();
        this.value = Integer.parseInt(elements[2].trim());
    }

    /**
     * Explain less than expression
     * @param states
     * @return
     */
    @Override
    public boolean interpret(Map<String, Integer> states) {
        if(!states.containsKey(key)) {
            return false;
        }
        int stateValue = states.get(key);
        return stateValue < value;
    }
}

And expression classes:

/**
 * And expression
 */
public class AndExpression implements Expression {
    private List<Expression> expressions = new ArrayList<>();

    public AndExpression(List<Expression> expressions) {
        this.expressions = expressions;
    }

    public AndExpression(String strAndExpression) {
        String[] strExpressions = strAndExpression.split("&&");
        for(String strExpr : strExpressions) {
            if(strExpr.contains(">=")) {
                expressions.add(new GreaterOrEqualExpression(strExpr));
            } else if(strExpr.contains("<")) {
                expressions.add(new LessExpression(strExpr));
            } else {
                throw new RuntimeException("Expression is invalid: " + strAndExpression);
            }
        }
    }

    /**
     * Interpretation and expression
     *
     * @param states
     * @return
     */
    @Override
    public boolean interpret(Map<String, Integer> states) {
        for(Expression expr : expressions) {
            if(!expr.interpret(states)) {
                return false;
            }
        }
        return true;
    }
}

Or expression class:

/**
 * Or expression
 */
public class OrExpression implements Expression {
    private List<Expression> expressions = new ArrayList<>();

    public OrExpression(List<Expression> expressions) {
        this.expressions = expressions;
    }

    public OrExpression(String strOrExpression) {
        String[] andExpressions = strOrExpression.split("\\|\\|");
        for(String andExpr : andExpressions) {
        	// &&The priority of expression is higher than 𞓜 expression. The & & expression must be evaluated before | expression is evaluated
            // Reuse & & expressions here
            expressions.add(new AndExpression(andExpr));
        }
    }

    /**
     * Interpretation or expression
     *
     * @param states
     * @return
     */
    @Override
    public boolean interpret(Map<String, Integer> states) {
        for(Expression expr : expressions) {
            if(expr.interpret(states)) {
                return true;
            }
        }
        return false;
    }
}

Affinity expression parsing class:

/**
 * Analysis of blind date expression
 */
public class BlindDateRuleInterpreter {

    private Expression expression;

    /**
     * Start with the or expression
     * @param ruleExpression
     */
    public BlindDateRuleInterpreter(String ruleExpression) {
        this.expression = new OrExpression(ruleExpression);
    }

    public boolean interpret(Map<String, Integer> states) {
        return expression.interpret(states);
    }
}

Client test class:

/**
 * Client test class
 */
public class ClientTest {
    public static void main(String[] args) {
        // In order to get closer to life, the key here is in Chinese
        String rule = "house >= 1 && annual income >= 20 && height >= 175 && Age < 30 && education >= 4 || property >= 1000";
        BlindDateRuleInterpreter interpreter = new BlindDateRuleInterpreter(rule);
        Map<String, Integer> states = new HashMap<>();
        states.put("house", 1);
        states.put("annual income", 20);
        states.put("height", 176);
        states.put("Age", 28);
        states.put("education", 4);
        states.put("property", 100);
        // Judge the dating condition according to the expression
        boolean qualified = interpreter.interpret(states);
        if(qualified) {
            System.out.println("Brother, Congratulations, you are qualified!");
        } else {
            System.out.println("Brother, we still need to continue our efforts!");
        }
    }
}

Output:

Brother, Congratulations, you are qualified!

summary

The Interpreter pattern describes how to define a grammar for a simple language, how to represent a sentence in the language, and how to interpret these sentences.

The core of the interpreter is to split the syntax parsing work into small classes, so as to avoid large and complete parsing classes. The general approach is to split the grammar rules into small independent units, analyze each unit, and finally merge them into the analysis of the whole grammar rules.

advantage

  • Interpreter is a simple syntax analysis tool. Its most significant advantage is extensibility. To modify the syntax rules, you only need to modify the corresponding non terminator expression. If you extend the syntax, you only need to add a non terminator class.

shortcoming

  • The interpreter mode will cause class expansion, and each syntax will generate a non terminator expression. When the syntax rules are complex, a large number of class files may be generated, which brings a lot of trouble for maintenance.
  • The Interpreter pattern may use a lot of loops and recursion. Efficiency is a problem that can not be ignored, especially when it is used to parse complex and lengthy syntax.

Postscript

Xiaomei: Hello, aunt. It's been three months. Why didn't you introduce a boy to me?

Matchmaker: Oh, there is no suitable one! I put the conditions of more than a dozen boys into the formula. Only one meets the conditions, and then the other party thinks your conditions are not good enough. Don't worry, I'll find it for you again.

Xiaomei: Er... I don't want the formula. As long as I have a good character and correct values, good character is the most important. Let's talk about other things when we meet.

Matchmaker: Well, it's easy to do. I'll pay attention to it.

Topics: Design Pattern computer