動作型設計モードの解釈器モード(Interpreter)


設計モードの解釈モード
インタプリタモードは主にコンパイラで使用され、言語を説明します.次に、4つの演算を実証し、インタプリタモードを演算に徐々に適用します.まず、設計モードを使用していない4つの演算のコード(再帰処理サブ式)を見てみましょう.
public class Interpreter_01 {

    private static final List<Character> symbols = new ArrayList<>(Arrays.asList('+','-','*','/','%','(',')'));
    public static void main(String[] args) {
        String expression = readExpression();
        Map<Character, Integer> expressionRelValue = readValue(expression);
        int result = run(expression, expressionRelValue);
        System.out.println("    ("+expression+")="+result);
    }

    private static String readExpression() {
        System.out.print("      :");
        Scanner scanner = new Scanner(System.in);
        return scanner.next();
    }

    private static Map<Character, Integer> readValue(String expression){
        Map<Character, Integer> expressionRelValue = new HashMap<>();
        if(null!=expression && !"".equals(expression)){
            char[] chars = expression.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                char var = chars[i];
                if(!symbols.contains(var)){
                    System.out.print(var+"=");
                    Scanner scanner = new Scanner(System.in);
                    expressionRelValue.put(var,Integer.valueOf(scanner.next()));
                }
            }
        }
        return expressionRelValue;
    }

    private static int run(String expression, Map<Character, Integer> expressionRelValue){
        char[] chars = expression.toCharArray();
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < chars.length; i++) {
            char var = chars[i];
            switch (var){
                case '+':
                    return stack.push(stack.pop()+run(expression.substring(i+1), expressionRelValue));
                case '-':
                    return stack.push(stack.pop()-run(expression.substring(i+1), expressionRelValue));
                case '(':
                    return stack.push(run(expression.substring(i+1), expressionRelValue));
                case ')':
                    break;
                default:
                    stack.push(expressionRelValue.get(var));
                    break;
            }
        }
        return stack.pop();
    }
}

実行結果:
式を入力してください:a+b+c-d a=2 b=3 c=4 d=1演算結果(a+b+c-d)=8
以上が最も簡単な設計で,クラスに依存することなく4則演算を完了した.このような設計は一時的な問題を解決するしかなく、後続の優雅な拡張をサポートしたい場合は、演算解析ロジック全体を再設計し、まず計算ロジックを分析する必要があります.
  • 演算記号であれば、必ず両方に数値または式があります.そうでなければ、式エラー
  • 前かっこ"("の場合、後かっこ")に遭遇するまで内部のサブエクスプレッションをループ処理する必要があり、サブエクスプレッション演算は
  • を終了する.
  • 演算が終了するたびにスタックに圧入する必要があり、次回の計算時にスタック
  • を出る.
    再設計後のコードは次のとおりです.
    public class Interpreter_02 {
    
        private static final List<Character> symbols = new ArrayList<>(Arrays.asList('+','-','*','/','%','(',')'));
        public static void main(String[] args) {
            String expression = readExpression();
            Map<Character, Integer> expressionRelValue = readValue(expression);
            AbstractExpression abstractExpression = run(expression, expressionRelValue);
            System.out.println("    ("+expression+")="+abstractExpression.interpreter(expressionRelValue));
        }
    
        private static String readExpression() {
            System.out.print("      :");
            Scanner scanner = new Scanner(System.in);
            return scanner.next();
        }
    
        private static Map<Character, Integer> readValue(String expression){
            Map<Character, Integer> expressionRelValue = new HashMap<>();
            if(null!=expression && !"".equals(expression)){
                char[] chars = expression.toCharArray();
                for (int i = 0; i < chars.length; i++) {
                    char var = chars[i];
                    if(!symbols.contains(var)){
                        System.out.print(var+"=");
                        Scanner scanner = new Scanner(System.in);
                        expressionRelValue.put(var,Integer.valueOf(scanner.next()));
                    }
                }
            }
            return expressionRelValue;
        }
    
        private static AbstractExpression run(String expression, Map<Character, Integer> expressionRelValue){
            char[] chars = expression.toCharArray();
            Stack<AbstractExpression> stack = new Stack<>();
            for (int i = 0; i < chars.length; i++) {
                char var = chars[i];
                switch (var){
                    case '+':
                        return stack.push(new AddExpression(stack.pop(),run(expression.substring(i+1), expressionRelValue)));
                    case '-':
                        return stack.push(new SubExpression(stack.pop(),run(expression.substring(i+1), expressionRelValue)));
                    case '(':
                        return stack.push(new LeftParenthesesExpression(run(expression.substring(i+1), expressionRelValue)));
                    case ')':
                        break;
                    default:
                        stack.push(new VarExpression(var));
                        break;
                }
            }
            return stack.pop();
        }
    
        abstract static class AbstractExpression{
            public abstract int interpreter(Map<Character, Integer> map);
        }
    
        static class VarExpression extends AbstractExpression{
            private char key;
    
            public VarExpression(char key) {
                this.key = key;
            }
    
            @Override
            public int interpreter(Map<Character, Integer> map) {
                return map.get(this.key);
            }
        }
    
        abstract static class SymbolExpression extends AbstractExpression{
            private AbstractExpression left;
            private AbstractExpression right;
    
            public SymbolExpression(AbstractExpression left, AbstractExpression right) {
                this.left = left;
                this.right = right;
            }
        }
    
        static class AddExpression extends SymbolExpression{
    
            public AddExpression(AbstractExpression left, AbstractExpression right) {
                super(left, right);
            }
    
            @Override
            public int interpreter(Map<Character, Integer> map) {
                return super.left.interpreter(map)+super.right.interpreter(map);
            }
        }
    
        static class SubExpression extends SymbolExpression{
    
            public SubExpression(AbstractExpression left, AbstractExpression right) {
                super(left, right);
            }
    
            @Override
            public int interpreter(Map<Character, Integer> map) {
                return super.left.interpreter(map)-super.right.interpreter(map);
            }
        }
    
        static class LeftParenthesesExpression extends SymbolExpression{
    
            public LeftParenthesesExpression(AbstractExpression right) {
                super(null, right);
            }
    
            @Override
            public int interpreter(Map<Character, Integer> map) {
                return super.right.interpreter(map);
            }
        }
        
    }
    

    再構成されたコードは,主に演算を2つの部分に分け,一部はkeyに対応する数値,一部は演算記号である.しかし,この2つの部分は最終的に同じ結果を返すため,同じ抽象クラスを継承し,それぞれの処理ロジックを実現する.これにより,単純な加減演算の解析が完了した.
    解釈モードの公式定義:言語を指定し、文法の表現を定義し、言語内の文を解釈する解釈器を定義します.
    メリット:
    拡張性に優れている
    欠点:
    クラス膨張デバッグが複雑ループ回数が大きすぎて再帰深さが深いほど効率が低下