10

CompositeRecursive Descendent Parser、およびInterpreterを使用して式評価器を作成するように依頼されました。

文法は次のとおりです。

<cond> → <termb> [OR <termb>]*
<termb>→&lt;factb>[AND <factb>]*
<factb>→&lt;expr> RELOP <expr> | NOT <factb> | OPAR <cond> CPAR
<expr> → [PLUS | MINUS] <term> [(PLUS <term>) | (MINUS <term>)]*
<term> → <termp> [(MULT <termp>) | (DIV <termp>) | (REM <termp>)]*
<termp> → <fact> [POWER <fact>]*
<fact> → ID | NUM | OPAR1 <expr> CPAR1
----TERMINALS----
ID → ("A" | ... | "Z" | "a" | ...| "z") [("A"| ... | "Z" | "a" | ...| "z" | "0" | ... | "9")]*
NUM → ("0" | ... | "9") [("0" | ... | "9")]*
OPAR → "("
CPAR → ")"
OPAR1 → "["
CPAR1 → "]"
RELOP → EQ | NEQ | GT | GE | LT | LE
EQ → "= ="
NEQ → "!="
GT → ">"
GE → ">="
LT → "<"
LE → "<="
POWER → "^"
DIV → "/"
REM → "%"
MULT → "*"
MINUS → "−"
PLUS → "+"
AND → “and” or “&amp;&”
OR → “or” or “||”
NOT → “not” or “!”

割り当ては次のとおりです。

Composite、Recursive Builder、および Interpreter に基づくプロジェクトの目標は、条件式を取得し、構文解析を行い、複合ツリーを構築することです。ツリーから始めて、内部変数の値を含む外部コンテキスト (プロパティ ファイルから読み取られる) に基づいて、条件の結果を評価する必要があります。

さて、私が最初に気付いたのは、InterpreterがComposite構造を使用していることです。そのため、evaluate(:Context)メソッドを使用してComposite構造を拡張することをお勧めします。

周りに聞いてみたのですが、これは課題のやり方ではないと言われました。Compositeツリーから始めて、 Interpreterツリーを構築したようです(作業するツリーが既にあるので、これは私にとってはナンセンスです!)。

そのため、 Composite + Recursive Builderを使用してツリーを構築しました。入力を認識し、問題なくツリーを構築します。

しかし問題は、 Interpreterを自分の構造に適用するにはどうすればよいかということです。

これが私のクラス図です(イタリア語ですが、かなり理解できます)

Composite+Builder クラス図

うまくいけば、Interpreterは文法規則ごとにクラスを使用するので、condクラス、次にtermbなどを作成する必要があります。

しかし、それらをコンポジットにリンクするにはどうすればよいでしょうか?

4

3 に答える 3

11

同じツリー構造を使用しないように言われた理由がわかりません。式インターフェイスにevaluate()メソッドを追加すると思います。それは私には理にかなっています。式は、それ自体を評価する方法を知っている必要があります。

現在の式インターフェイスは、(オペランドなど)公開しすぎていると言えます。式のクライアントとして、私は1)それを呼び出し、2)結果を読み、おそらく3)それを印刷する必要があるだけです。実際、私は直接印刷するよりもtoString()を使用する方が好きです。

すでにお気づきかもしれませんが、すべての式が2つのオペランド(NOTやNEGATEなど)を使用しているわけではありません。これはすでにあなたのインターフェースとのある種の矛盾を生み出しています。私はそれを次のように単純化します:

 public interface Expression {
   int evaluate();
 }

次に、操作と端末のそれぞれが、自分自身を評価する(そして自分自身を文字列に変換する)方法を知っています。

したがって、次のような具体的な操作を行うことができます。

 public class Terminal implements Expression {
   private final int value;

   public Terminal(int value) { this.value = value; }

   public int evaluate() { return value; }

   public String toString() { return String.valueOf(value); }
 }

 public class Add implements Expression {
   private final Expression left;
   private final Expression right;

   public Add(Expression left, Expression right) {
     this.left = left;
     this.right = right;
   }

   public String toString() {
     return left.toString() + " + " + right.toString();
   }

   // leave the rest for you
 }

今、私はかなり簡単にツリーを構築することができます

Expression expr = new Add(new Terminal(1), new Subtract(new Terminal(2), new Terminal(3)));

int result = expr.evaluate();
System.out.print(expr.toString() + " = " + result);

また、個々のオペランドに直接アクセスする必要もありません。

于 2012-08-20T17:00:41.773 に答える
3

私があなたの問題を正しく理解していれば、すべての具象クラスはメインの複合構造のサブクラスである必要があると言えます。

Expression がメイン コンポジットの場合:

式 : Term 式 : OperandTerm

条件: Term BinOperand 式 条件: UnaryOperand 式

用語: 整数 | 脂肪 | ...

. . .

于 2012-08-18T12:04:06.673 に答える
2

インタープリターは、端末オブジェクトではなく、端末に基づいて言語の式を定義する方法を提供します。インタープリターは複合パターンそのものなので、すでに適用されていると思います。

おそらく、notTerminal 要素と終端要素ごとに 1 つのクラスを作成する必要はありません。文法記号とは異なるように、NonTerminal/Terminal クラスで (operatorType, expressionType) などの属性を使用します。

[ A = 0 ] のような文法で生成された式が与えられた場合、インタープリターのパターン クラスで形成されたオブジェクト ツリーは次のようになります (品質の悪さと UML シンタックス エラーについてはご容赦ください。ただし、現在適切な UML エディターがありません)。

ここに画像の説明を入力

このオブジェクト ツリーは、式アナライザーによって構築する必要があります。このツリーを取得したら、Recursive Descendent Parser を使用して、このツリーをウォークスルーし、各ツリー ノードを評価します。

したがって、式の評価はパーサーによって行われ、インタープリターは文法式を表すデータ構造を提供します。

この助けを願っています。

于 2012-08-20T10:36:48.227 に答える