動作型設計モードの解釈器モード(Interpreter)
43034 ワード
設計モードの解釈モード
インタプリタモードは主にコンパイラで使用され、言語を説明します.次に、4つの演算を実証し、インタプリタモードを演算に徐々に適用します.まず、設計モードを使用していない4つの演算のコード(再帰処理サブ式)を見てみましょう.
実行結果:
式を入力してください:a+b+c-d a=2 b=3 c=4 d=1演算結果(a+b+c-d)=8
以上が最も簡単な設計で,クラスに依存することなく4則演算を完了した.このような設計は一時的な問題を解決するしかなく、後続の優雅な拡張をサポートしたい場合は、演算解析ロジック全体を再設計し、まず計算ロジックを分析する必要があります.演算記号であれば、必ず両方に数値または式があります.そうでなければ、式エラー 前かっこ"("の場合、後かっこ")に遭遇するまで内部のサブエクスプレッションをループ処理する必要があり、サブエクスプレッション演算は を終了する.演算が終了するたびにスタックに圧入する必要があり、次回の計算時にスタック を出る.
再設計後のコードは次のとおりです.
再構成されたコードは,主に演算を2つの部分に分け,一部はkeyに対応する数値,一部は演算記号である.しかし,この2つの部分は最終的に同じ結果を返すため,同じ抽象クラスを継承し,それぞれの処理ロジックを実現する.これにより,単純な加減演算の解析が完了した.
解釈モードの公式定義:言語を指定し、文法の表現を定義し、言語内の文を解釈する解釈器を定義します.
メリット:
拡張性に優れている
欠点:
クラス膨張デバッグが複雑ループ回数が大きすぎて再帰深さが深いほど効率が低下
インタプリタモードは主にコンパイラで使用され、言語を説明します.次に、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つの部分は最終的に同じ結果を返すため,同じ抽象クラスを継承し,それぞれの処理ロジックを実現する.これにより,単純な加減演算の解析が完了した.
解釈モードの公式定義:言語を指定し、文法の表現を定義し、言語内の文を解釈する解釈器を定義します.
メリット:
拡張性に優れている
欠点:
クラス膨張デバッグが複雑ループ回数が大きすぎて再帰深さが深いほど効率が低下