수색…


통사론

  • 선택기 일치 partialFunction
  • 선택기 일치 (대소 문자 바꾸기 목록) // 이것은 위의 가장 일반적인 형식입니다

매개 변수

매개 변수 세부
선택자 값이 패턴 일치 된 표현식입니다.
대안들 case 별 대체 목록.

간단한 패턴 일치

이 예는 입력 값을 여러 값과 비교하는 방법을 보여줍니다.

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> )를 처리하는 것이 가장 좋은 함수 프로그래밍 연습입니다. 케이스 클래스 를 매치하면 케이스가없는 경우 컴파일러가 경고를 생성하는 데 도움이 될 수 있습니다. 봉인 된 특성을 확장하는 사용자 정의 유형에서도 마찬가지입니다. 일치가 총합이면 기본 사례가 필요하지 않을 수 있습니다.

또한 인라인으로 정의되지 않은 값과 일치시킬 수도 있습니다. 이것들은 대문자로 된 이름을 사용하거나 backticks를 묶어 얻어지는 안정된 식별자 여야합니다.

Onetwo 가 다른 곳에서 정의되거나 함수 매개 변수로 전달 된 경우 :

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"

안정적인 식별자로 패턴 매칭

표준 패턴 일치에서 사용되는 식별자는 둘러싼 범위의 식별자를 무시합니다. 때로는 엔 클로징 스코프의 변수를 매치 할 필요가있다.

다음 예제 함수는 문자와 튜플 목록을 가져 와서 새로운 튜플 목록을 반환합니다. 문자가 튜플 중 하나의 첫 번째 요소로 존재하면 두 번째 요소가 증가합니다. 목록에 아직없는 경우 새 튜플이 만들어집니다.

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) => ...

스칼라는 눈금이 표시된 모든 변수를 안정적인 식별자로 해석합니다. 동일한 방법으로 대문자로 시작하는 변수도 해석합니다.

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( ...)+: 에 제약 Seq( ...) 하십시오.

:: 를 사용하는 동안 WrappedArray , Vector 등에서는 작동하지 않습니다. 참고 :

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 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
}

이것은 fold , map / getOrElse 를 사용하는 것과 기능면에서 동일합니다 :

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

패턴 매칭 밀폐 된 특성

타입이 봉인 된 형질 인 객체를 패턴 매칭 할 때, 스칼라는 컴파일 타임에 모든 경우가 '철저하게 일치'하는지 검사 할 것입니다 :

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 문에서 컴파일러 경고가 throw됩니다. 이렇게하면 철저한 리팩토링이 쉬워집니다. 컴파일러는 개발자에게 업데이트해야하는 모든 코드를 알려줍니다.

정규식과의 패턴 일치

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 도 추출되지만이 예제에서는 아무 것도 수행되지 않습니다. String 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

이 방식으로 전환 기능이없는 고전적인 '스위치'문과 유사합니다. 그러나 문제의 유형에서 패턴 일치 및 '추출'값을 지정할 수도 있습니다. 예를 들면 :

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 경우 밑줄 ( _ )을 사용하여 언 바운드 변수와 일치시킵니다. 즉 값 (이 경우 Hadas27 )은 이름에 바인딩되어 있지 않으므로 해당 경우 처리기에서 사용할 수 없습니다. 이는 값이 무엇인지 걱정하지 않고 '모든'값을 일치시키기 위해 유용한 속기입니다.

tableswitch 또는 lookupswitch로 컴파일 된 패턴 매칭

@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"

스칼라 문서 (2.8+) - @switch :

일치 식에 적용 할 주석입니다. 이 플래그가 있으면 컴파일러는 일치 항목이 테이블 스위치 또는 조회 스위치로 컴파일되었는지 확인하고 대신 일련의 조건식으로 컴파일하면 오류가 발생합니다.

Java 사양 :

  • tableswitch : "색인으로 점프 테이블에 액세스하고 점프"
  • lookupswitch : "키 매치 및 점프에 의한 점프 테이블 액세스"

한 번에 여러 패턴 매치

| 동일한 결과를 산출하기 위해 여러 입력에 대해 단일 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")
  }
}

첫 번째 경우는 특정 문자열과 일치시켜 해당 가격을 얻는 방법을 보여줍니다. 두 번째 경우는 if와 tuple 추출 을 사용하여 튜플의 요소와 일치시킵니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow