2

私は簡単なパーサーを書いていますが、Scala です。

ファイル内の要素を表す基本特性があります。

trait Token[T] {
    def stringValue: String
    def value: T
}

これが私が必要とするものです - 文字列 (テキスト) 値と解析された値 (同じ文字列になることがあります)。ここで、次のサブクラスのセットが必要です。

  • 予約済みの記号/キーワードclassなどvoid
  • など+の特殊記号/
  • 123 などの整数リテラル
  • 1.23 などの実数リテラル
  • '123'などの文字列リテラル

このような階層をどのように実装しますか? これは有限なので、ケースクラスを使用するといいでしょう..と思います。しかし、列挙も素晴らしいでしょう..どのように組み合わせるのですか?


言い換えれば、これ (以下) を Scala でもっと Scal っぽい方法で書くにはどうすればよいでしょうか?

public interface Token<T> {
    String stringValue();
    T value();
}

public enum ReservedSymbol implements Token<ReservedSymbol> {
    CLASS('class'), VOID('void');

    private String val;
    private ReservedSymbol(String val) { this.val = val; }

    public String stringValue() { return val; }
    public ReservedSymbol value() { return this; }
}


public class IntegerLiteral implements Token<Integer> {       
    private Integer val;
    public IntegerLiteral(String val) { this.val = Integer.valueOf(val); }

    public String stringValue() { return val.toString(); }
    public Integer value() { return val; }
}

4

2 に答える 2

5

Scala でそのような階層を構築するときは、次の原則を適用するようにしてください。

  1. 必要なクラス階層をスケッチします。リーフ ノードのみがインスタンス化され、内部ノードが抽象化されるように設計します。
  2. 内部ノードを特性として実装する
  3. リーフ ノードをケース クラスとして実装する

その理由は、case クラスが多くの便利な魔法 (toString、unapply、serialize、equals など) を自動的に追加するためです。しかし、必要なコードは、ケース クラス間の継承と互換性のない離れた場所で生成されます (たとえば、equals は正しく機能しません)。

通常、パラメータのないリーフ タイプは としてcase objectモデル化され、パラメータを持つリーフ タイプは としてモデル化されcase classます。

型ツリーの内部ノードをインスタンス化する必要がある場合は、人工リーフを追加してケース クラス/ケース オブジェクトとして実装するだけです。

Scala で列挙型を使用することもできますが、通常はケース クラスの方が実用的です。特定の文字列を対応する列挙型に変換する必要がある場合は、通常、列挙型が適しています。でこれを行うことができますEnumeration.withName(String)

Alexey Romanov からの回答では、この原則を 1 つのルート ノードと 3 つのリーフ ノードを持つ型ツリーに適用する方法を確認できます。

  1. Token(内部ノード => 特性)

    1.1。ClassSymbol(パラメーターなしのリーフ ノード => ケース オブジェクト)

    1.2. VoidSymbol(パラメーターなしのリーフ ノード => ケース オブジェクト)

    1.3。IntegerLiteral. (パラメーターを持つリーフ ノード => ケース クラス)

列挙型とケースクラスの両方を使用した状況の例:

trait Token[T]{
  def stringValue: String 
  def value: T
}

object ReservedSymbolEnum extends Enumeration {
  type ReservedSymbolEnum = Value
  val `class`, `void` = Value
      val NullValue = Value("null") // Alternative without quoting
}

case class ReservedSymbol(override val stringValue: String)extends Token[ReservedSymbolEnum.ReservedSymbolEnum] {
  def value = ReservedSymbolEnum.withName(stringValue)
}

case class StringLiteral(override val stringValue: String) extends Token[String] {
  override def value = stringValue
}

case class IntegerLitaral(override val stringValue: String) extends Token[Int] {
  override def value = stringValue.toInt
}

いくつかの使用例:

scala> def `void`=ReservedSymbol("void")
void: ReservedSymbol

scala> `void`.value
res1: ReservedSymbolEnum.Value = void

scala> def `42`=IntegerLiteral("42")
42: IntegerLitaral

scala> `42`.value
res2: Int = 42
于 2013-09-16T11:32:25.963 に答える