9

ケースクラスを使用して実装されたいくつかの異なるタイプの頂点を持つグラフのような構造を表す単純なクラス階層があります。

sealed trait Node

sealed abstract case class Vertex extends Node
case class Arc extends Node

case class VertexType1 (val a:Int) extends Vertex
case class VertexType2 (val b:Int) extends Vertex

これにより、次のようなマッチ ブロックを記述できます。

def test (x: Node) = x match {
  case _ : Arc => "got arc"
  case _ : Vertex => "got vertex"
}

またはこのように:

def test (x: Node) = x match {
  case _ : Arc => "got arc"
  case c : Vertex => c match {
    case _ : VertexType1(a) => "got type 1 vertex " + a
    case _ : VertexType2(a) => "got type 2 vertex " + a
  }
}

この実装には次のプロパティがあることに注意してください。

1) アークと頂点を区別する一致ブロックを記述できますが、特定の頂点タイプを区別するのではなく、頂点タイプを区別する一致ブロックも記述できます。

2) 頂点タイプ固有のマッチ ブロックと非頂点タイプ固有のマッチ ブロックの両方で、パターン マッチングの網羅性がチェックされます。

ただし、ケース クラスからの継承は非推奨であり、コンパイラは代わりにエクストラクタを使用して非リーフ ノードでのマッチングをサポートすることを提案します (つまり、上記の例では、アークと頂点を区別し、頂点タイプを区別しません)。

問題: ケース クラスの継承を使用せずに同様のクラス階層を実装することは可能ですが、上記の両方のユース ケースでコンパイラによってパターンの網羅性チェックが実行されますか?

編集: VertexType クラスにコンストラクター パラメーターを追加して、型に対してのみ一致が実行されないようにしました。

ケースクラスを使用しない現在の実装は次のとおりです。

sealed trait Node

sealed abstract class Vertex extends Node
class Arc extends Node

class VertexType1 (val a:Int) extends Vertex
class VertexType2 (val b:Int) extends Vertex

object VertexType1 {
  def unapply (x : VertexType1) : Some[Int] = Some(x.a)
}

object VertexType2 {
  def unapply (x : VertexType2) : Some[Int] = Some(x.b)
}

そしてテストコード:

def test (x: Node) = x match {
  case _ : Arc => "got arc" 
  case v : Vertex => v match {
    case VertexType1(a) => "got vertex type 1 " + a 
  }
}

2 番目のブロック (VertexType2 が一致することはありません) で非網羅的な一致に関する警告が表示されることを期待していますが、それはありません。

実際、2.9.0-RC3 より前の Scala コンパイラでは警告が表示されるはずですが、RC3 以降のバージョン (2.9.0 および 2.9.0-1 を含む) では警告が表示されず、かなり混乱しています。

4

3 に答える 3

2

通常、これは実行できません。

scalacシールされたクラスは、コンパイル時に一致する可能性のある数を知っているため、特別なケースです (しゃれは意図されていません) 。

しかし、エクストラクタを使用すると任意のコードを実行でき、停止の問題が発生するため、コンパイラがすべてのケースですべてのケースをチェックすることを保証する方法はありません。検討:

def test(foo: Int) {
  foo match {
    case IsMultipleOf8(n) => printf("%d was odd\n", n)
  }
}

8 の倍数ではない数を処理しないため、これは網羅的ではありませんが、コンパイラは (すべてIntの でエクストラクタを実行しないと) タイプの一部の値のみIntが 8 の倍数であると推測できません。

于 2011-06-14T13:24:51.360 に答える
2

エクストラクタは、scala のケース クラスのようにパターン マッチングで使用する可能性を提供しますが、ケース修飾子を使用するときに得られる他の標準的な実装はありません。しかし、これらの余分な実装 (特に equals の実装) は、ケース クラスの継承を危険にするものであり、非推奨になりました。

ただし、シール クラスは直交する機能であり、ケース クラスまたはエクストラクタがあるかどうかに関係なく使用できます。エクストラクタを使用すると、その場で標準実装を取得することはできませんが、エクストラクタを継承することができます。エクストラクタは、una​​pply および/または unapplySeq メソッドを持つ単純な通常のクラスです。

この例で行ったことは、型のパターン マッチングと呼ばれます。これだけを行いたい場合は、ケース クラスもエクストラクタも必要ありません。必要なすべてのクラスでそれを行うことができます。そのため、ケース修飾子を簡単に削除できます。

結論として、パターンの網羅性は封印されたクラス階層によって達成されます。パターン マッチングは、エクストラクタとケース クラスによって実現されます。後者は、頻繁に使用される関数の魅力的な標準実装を備えた単なるエクストラクタです。これが役に立ったことを願っています。

于 2011-06-13T17:45:31.830 に答える
0

scala-lang.org からの引用:

パターン マッチのセレクターがシール クラスのインスタンスである場合、パターン マッチのコンパイルは、指定されたパターンのセットが完全ではないこと、つまり、実行時に MatchError が発生する可能性があることを診断する警告を発する可能性があります。

于 2011-06-13T16:47:13.060 に答える