サーチ…


構文

  • セレクタマッチpartialFunction
  • セレクタの一致(大文字/小文字のリスト)//これは上記の中で最も一般的な形式です

パラメーター

パラメータ詳細
セレクタ値がパターンマッチングされている式。
代替案 casecase代替リスト。

シンプルなパターンマッチ

次の例は、入力を複数の値と照合する方法を示しています。

def f(x: Int): String = x match {
  case 1 => "One"
  case 2 => "Two"
  case _ => "Unknown!"
}

f(2)  // "Two"
f(3)  // "Unknown!"

ライブデモ

注: _は、 フォールスルーまたはデフォルトの場合ですが、必須ではありません。

def g(x: Int): String = x match {
  case 1 => "One"
  case 2 => "Two"
}

g(1)  // "One"
g(3)  // throws a MatchError

例外を投げるのを避けるために、デフォルトのケース( case _ => <do something> )を処理するのがここでは最高の関数プログラミングの練習です。 caseクラスが一致しいれば、コンパイラが警告を生成するのに役立つことに注意してください。密封された特性を拡張するユーザー定義のタイプの場合も同じです。マッチが合計である場合、デフォルトのケースは必要ないかもしれません

また、インラインで定義されていない値と照合することもできます。これらは、 安定した識別子でなければなりません。これは、大文字の名前を使用するか、バッククォートを囲むかのいずれかによって取得されます。

One two他の場所で定義されているか、関数のパラメータとして渡されている:

val One: Int = 1
val two: Int = 2

それらは次の方法で照合することができます:

def g(x: Int): String = x match {
  case One => "One"
  case `two` => "Two"
}

例えばJavaのような他のプログラミング言語とは異なり、転倒はありません。 caseブロックが入力と一致すると、それが実行され、一致が完了します。したがって、最も特殊なケースは最後のケースブロックでなければなりません。

def f(x: Int): String = x match {
  case _ => "Default"
  case 1 => "One"
}

f(5) // "Default"
f(1) // "Default"

安定した識別子によるパターンマッチング

標準的なパターンマッチングでは、使用される識別子は、囲みスコープ内の識別子をシャドウします。場合によっては、囲みスコープの変数を照合する必要があります。

次の関数例は、文字とタプルのリストを取り、タプルの新しいリストを返します。文字がタプルの1つの最初の要素として存在する場合、2番目の要素がインクリメントされます。リストにまだ存在しない場合は、新しいタプルが作成されます。

def tabulate(char: Char, tab: List[(Char, Int)]): List[(Char, Int)] = tab match {
  case Nil => List((char, 1))
  case (`char`, count) :: tail => (char, count + 1) :: tail
  case head :: tail => head :: tabulate(char, tail)
}

上の例は、メソッドの入力charがパターンマッチで '安定'に保たれているパターンマッチングを示しています。つまり、 tabulate('x', ...)を呼び出すと、最初のcase文は次のように解釈されます。

case('x', count) => ...

Scalaは、ティックマークで区切られた変数を安定した識別子として解釈します。同じように大文字で始まる変数も解釈します。

Seqのパターンマッチング

コレクション内の要素の正確な数を確認するには

def f(ints: Seq[Int]): String = ints match {
  case Seq() =>
      "The Seq is empty !"
  case Seq(first) =>
      s"The seq has exactly one element : $first"
  case Seq(first, second) =>
      s"The seq has exactly two elements : $first, $second"
  case  s @ Seq(_, _, _) => 
      s"s is a Seq of length three and looks like ${s}"  // Note individual elements are not bound to their own names.
  case s: Seq[Int] if s.length == 4 =>
      s"s is a Seq of Ints of exactly length 4"  // Again, individual elements are not bound to their own names.
  case _ =>
      "No match was found!"
}

ライブデモ

最初の要素を抽出し、残りをコレクションとして保持するには:

def f(ints: Seq[Int]): String = ints match {
  case Seq(first, second, tail @ _*) =>
      s"The seq has at least two elements : $first, $second. The rest of the Seq is $tail"
  case Seq(first, tail @ _*) =>
      s"The seq has at least one element : $first. The rest of the Seq is $tail"
  // alternative syntax
  // here of course this one will never match since it checks
  // for the same thing as the one above
  case first +: tail =>
      s"The seq has at least one element : $first. The rest of the Seq is $tail"
  case _ =>
      "The seq didn't match any of the above, so it must be empty"
}

一般に、配列を構築するために使用することができる任意の形態を用いて、既存の配列とのパターンマッチングを行うことができる。

Nil::を使用している間、シーケンスにパターンマッチングすると機能しますが、 List変換して予期しない結果が生じることに注意してください。これを避けるには、 Seq( ...)+:に自分自身を制限します。

WrappedArrayVectorなどでは::を使用することはできません。

scala> def f(ints:Seq[Int]) = ints match {
     | case h :: t => h
     | case _ => "No match"
     | }
f: (ints: Seq[Int])Any

scala> f(Array(1,2))
res0: Any = No match

そして+:

scala> def g(ints:Seq[Int]) = ints match {
     | case h+:t => h
     | case _ => "No match"
     | }
g: (ints: Seq[Int])Any

scala> g(Array(1,2).toSeq)
res4: Any = 1

ガード(式の場合)

case文をif式と組み合わせて、パターンマッチング時に余分なロジックを提供することができます。

def checkSign(x: Int): String = {
    x match {
      case a if a < 0 => s"$a is a negative number"
      case b if b > 0 => s"$b is a positive number"
      case c => s"$c neither positive nor negative"
    }
}

あなたのガードが非徹底的なマッチを作らないようにすることが重要です(コンパイラはしばしばこれをキャッチしません):

def f(x: Option[Int]) = x match {
    case Some(i) if i % 2 == 0 => doSomething(i)
    case None    => doSomethingIfNone
}

奇数にMatchErrorがスローされます。すべてのケースについて説明するか、ワイルドカードの大文字と小文字を区別するケースを使用する必要があります。

def f(x: Option[Int]) = x match {
    case Some(i) if i % 2 == 0 => doSomething(i)
    case _ => doSomethingIfNoneOrOdd
}

ケースクラスによるパターンマッチング

すべてのcaseクラスは、パターンマッチングの際にcaseクラスのメンバを取得するために使用できるextractorを定義します。

case class Student(name: String, email: String)

def matchStudent1(student: Student): String = student match {
    case Student(name, email) => s"$name has the following email: $email" // extract name and email
}

パターンマッチングのすべての通常のルールが適用されます。ガードと定数式を使用してマッチングを制御できます。

def matchStudent2(student: Student): String = student match {
    case Student("Paul", _) => "Matched Paul" // Only match students named Paul, ignore email
    case Student(name, _) if name == "Paul" => "Matched Paul" // Use a guard to match students named Paul, ignore email
    case s if s.name == "Paul" => "Matched Paul" // Don't use extractor; use a guard to match students named Paul, ignore email
    case Student("Joe", email) => s"Joe has email $email" // Match students named Joe, capture their email
    case Student(name, email) if name == "Joe" => s"Joe has email $email" // use a guard to match students named Joe, capture their email
    case Student(name, email) => s"$name has email $email." // Match all students, capture name and email 
}

オプションでのマッチング

Optionタイプでマッチングしている場合:

def f(x: Option[Int]) = x match {
    case Some(i) => doSomething(i)
    case None    => doSomethingIfNone
}

これはfoldmap / getOrElseを使うことと機能的に同等getOrElse

def g(x: Option[Int]) = x.fold(doSomethingIfNone)(doSomething)
def h(x: Option[Int]) = x.map(doSomething).getOrElse(doSomethingIfNone)

パターンマッチング密閉形質

型が密封された特性であるオブジェクトをパターンマッチングすると、Scalaはコンパイル時にすべてのケースが「徹底的に一致している」ことをチェックします:

sealed trait Shape
case class Square(height: Int, width: Int) extends Shape
case class Circle(radius: Int) extends Shape
case object Point extends Shape


def matchShape(shape: Shape): String = shape match {
    case Square(height, width) => "It's a square"
    case Circle(radius)        => "It's a circle"
    //no case for Point because it would cause a compiler warning.
}

Shape新しいcase classが後で追加されると、 Shapeすべてのmatch文がコンパイラの警告をスローし始めます。これにより、完全なリファクタリングが容易になります。コンパイラは、更新が必要なすべてのコードを開発者に警告します。

Regexとのパターンマッチング

val emailRegex: Regex = "(.+)@(.+)\\.(.+)".r

"[email protected]" match {
  case emailRegex(userName, domain, topDomain) => println(s"Hi $userName from $domain")
  case _ => println(s"This is not a valid email.")
}

この例では、正規表現は提供された電子メールアドレスに一致するように試みます。そうであれば、 userNamedomainが抽出されて出力されます。 topDomainも抽出されますが、この例では何も行われません。文字str .r呼び出しは、 new Regex(str)と同じです。 r関数は、 暗黙の変換によって使用できます。

パターンバインダー(@)

@記号は、パターンマッチング中に変数を名前にバインドします。バインドされた変数は、一致したオブジェクト全体または一致したオブジェクトの一部のいずれかです。

sealed trait Shape
case class Rectangle(height: Int, width: Int) extends Shape
case class Circle(radius: Int) extends Shape
case object Point extends Shape

(Circle(5): Shape) match {
  case Rectangle(h, w) => s"rectangle, $h x $w."
  case Circle(r) if r > 9 => s"large circle"
  case c @ Circle(_) => s"small circle: ${c.radius}"  // Whole matched object is bound to c
  case Point => "point"
}

> res0: String = small circle: 5

バインドされた識別子は、条件付きフィルタで使用できます。従って:

case Circle(r) if r > 9 => s"large circle"

次のように書くことができます:

case c @ Circle(_) if c.radius > 9 => s"large circle"

名前は、一致するパターンの一部にのみバインドできます。

Seq(Some(1), Some(2), None) match {
  // Only the first element of the matched sequence is bound to the name 'c'
  case Seq(c @ Some(1), _*) => head
  case _ => None
}

> res0: Option[Int] = Some(1)

パターンマッチングタイプ

isInstanceOf[B]ではなく、パターンマッチングを使用してインスタンスのタイプをチェックすることもできます。

val anyRef: AnyRef = ""
                                                  
anyRef match {
  case _: Number       => "It is a number"
  case _: String       => "It is a string"
  case _: CharSequence => "It is a char sequence"
}
//> res0: String = It is a string

ケースの順序は重要です。

anyRef match {
  case _: Number       => "It is a number"
  case _: CharSequence => "It is a char sequence"
  case _: String       => "It is a string"
}
//> res1: String = It is a char sequence

このように、フォールスルー機能を持たない古典的な 'switch'ステートメントに似ています。しかし、問題のタイプからパターンマッチと '抽出'値を組み合わせることもできます。例えば:

case class Foo(s: String)
case class Bar(s: String)
case class Woo(s: String, i: Int)

def matcher(g: Any):String = {
  g match {
    case Bar(s) => s + " is classy!" 
    case Foo(_) => "Someone is wicked smart!"
    case Woo(s, _) => s + " is adventerous!"
    case _ => "What are we talking about?"
  }
}

print(matcher(Foo("Diana")))  // prints 'Diana is classy!'
print(matcher(Bar("Hadas")))  // prints 'Someone is wicked smart!'
print(matcher(Woo("Beth", 27)))   // prints 'Beth is adventerous!'
print(matcher(Option("Katie")))  // prints 'What are we talking about?'

FooWoo場合、アンダースコア( _ )を使用して 'unbound variableと一致させる'ことに注意してください。つまり、値(この場合はHadas27 )は名前にバインドされていないため、その場合のハンドラでは使用できません。これは、その値が何であるかを気にせずに「任意の」値に一致させるために便利な省略形です。

テーブルスイッチまたはルックアップスイッチとしてコンパイルされたパターンマッチング

@switchアノテーションは、バイトコードレベルでmatchステートメントを単一のtableswitch命令に置き換えることができることをコンパイラに指示します。これは、実行時に不必要な比較や変数の負荷を取り除くことができるマイナーな最適化です。

@switchアノテーションは、リテラル定数とfinal val識別子との一致に対してのみ機能します。パターンマッチをtableswitch / lookupswitchとしてコンパイルできない場合、コンパイラは警告を発します。

import annotation.switch

def suffix(i: Int) = (i: @switch) match {
  case 1 => "st"
  case 2 => "nd"
  case 3 => "rd"
  case _ => "th"
}

結果は通常のパターンマッチと同じです:

scala> suffix(2)
res1: String = "2nd"

scala> suffix(4)
res2: String = "4th"

Scalaドキュメント (2.8以降)から@switch

一致式に適用される注釈。存在する場合、コンパイラは、マッチがテーブルスイッチまたはルックアップスイッチにコンパイルされていることを確認し、代わりに一連の条件式にコンパイルするとエラーを発行します。

Java仕様から:

  • テーブルスイッチ :「インデックスとジャンプによるジャンプテーブルへのアクセス」
  • lookupswitch : "キーマッチとジャンプによるジャンプテーブルへのアクセス"

一度に複数のパターンを一致させる

|同じ結果を得るために複数の入力に対して1つのcase文を一致させるために使うことができます:

def f(str: String): String = str match {
  case "foo" | "bar" => "Matched!"
  case _ => "No match."
}

f("foo")  // res0: String = Matched!
f("bar")  // res1: String = Matched!
f("fubar")  // res2: String = No match.

この方法でをマッチングするとうまくいくが、次のタイプのマッチングによって問題が発生することに注意してください。

sealed class FooBar
case class Foo(s: String) extends FooBar
case class Bar(s: String) extends FooBar

val d = Foo("Diana")
val h = Bar("Hadas")

// This matcher WILL NOT work.
def matcher(g: FooBar):String = {
  g match {
    case Foo(s) | Bar(s) => print(s)  // Won't work: s cannot be resolved
    case Foo(_) | Bar(_) => _         // Won't work: _ is an unbound placeholder
    case _ => "Could not match"
  }
}

後者の場合( _ )、バインドされていない変数の値を必要とせず、他の何かを実行したい場合は、上手くいく:

def matcher(g: FooBar):String = {
  g match {
    case Foo(_) | Bar(_) => "Is either Foo or Bar."  // Works fine
    case _ => "Could not match"
  }
}

それ以外の場合は、あなたのケースを分割して残します:

def matcher(g: FooBar):String = {
  g match {
    case Foo(s) => s 
    case Bar(s) => s
    case _ => "Could not match"
  }
}

タプルのパターンマッチング

以下のタプルのListを与えられます:

val pastries = List(("Chocolate Cupcake", 2.50), 
                    ("Vanilla Cupcake", 2.25),
                    ("Plain Muffin", 3.25))

パターンマッチングを使用すると、各要素を別々に処理できます。

pastries foreach { pastry =>
  pastry match {
    case ("Plain Muffin", price) => println(s"Buying muffin for $price")
    case p if p._1 contains "Cupcake" => println(s"Buying cupcake for ${p._2}")
    case _ => println("We don't sell that pastry")
  }
}

最初のケースでは、特定の文字列と照合して対応する価格を取得する方法を示しています。 2番目のケースでは、 タプルの要素と一致する ifおよびtuple抽出の使用を示しています。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow