Skip to content
chomg edited this page Aug 29, 2016 · 3 revisions

C/C++/Java의 switch/case 문과 유사하나, 더 다양한 기능을 제공함

4.1 단순매치

val bools = Seq(true, false)

for (bool <- bools) {
  bool match {
    case true => println("Got heads")
    case false => println("Got tails")
  }
}

bool의 값이 true, false임에 따라서, 각각의 case에 맞는 명령이 실행됨

4.2 매치 내의 값, 변수, 타입

for {
  x <- Seq(1, 2, 2.7, 'four)                                         // <1>
} {
  val str = x match {                                                // <2>
    case 1          => "int 1"                                       // <3>
    case i: Int     => "other int: "+i                               // <4>
    case _: Double  => "a double: "+x                                // <5>
    case unexpected => "unexpected value: " + unexpected             // <8>
  }
  println(str)                                                       // <9>
}

출력결과는 아래와 같다.

int 1
other int: 2
a double 2.7
unexpected value: 'four

Int의 특정한 값, 혹은 특정한 type 등을 적을 수 있다. <5>행처럼 변수이름을 위치지정자 _로 적는 경우에는, 변수 x를 그대로 사용하면 된다.

case는 적힌 순서에 따라 매칭이 된다. 예를 들어 <4>행이 <3>행보다 앞에 있으면, Int 1도 <4>행의 조건에 매치가 된다. 따라서 구체적인 값일수록 앞쪽 case에서 명시되어야 한다. 또한 PartialFunction이 아닌 경우, 매치는 완전해야하기 때문에, 입력이 Any라면 맨 마지막에 case _ 혹은 case variable_name처럼 기본 절을 추가해야 된다.

그리고 case에서 정의하는 어떤 변수가 아닌, 주어진 scope내의 변수값을 참고하기 싶을 때는 변수 이름 앞뒤에 ``를 붙여야 한다.

4.3 시퀀스에 일치시키기

Seq는 정해진 순서대로 원소를 순회할 수 있는 List나 Vector 등 모든 구체적인 컬렉션 타입의 부모 타입이다. 패턴매칭과 재귀를 사용해서 Seq를 순회하는 전통적인 관용구에 따라, Seq의 item들을 순회할 수 있다. Map의 경우는 순회 시 특별한 순서를 보장하지 않기 때문에, Seq의 서브타입은 아니고, Map.toSeq를 호출해서 키-값 튜플을 만들 필요가 있다.

val nonEmptySeq    = Seq(1, 2, 3, 4, 5)
val emptySeq       = Seq.empty[Int]
val nonEmptyMap    = Map("one" -> 1, "two" -> 2, "three" -> 3)

def seqToString2[T](seq: Seq[T]): String = seq match {
  case head +: tail => s"($head +: ${seqToString2(tail)})"           // <1>
  case Nil => "(Nil)"
}

for (seq <- Seq(nonEmptySeq, emptySeq, nonEmptyMap.toSeq)) {
  println(seqToString2(seq))
}

출력결과

(1 +: (2 +: (3 +: (4 +: (5 +: (Nil))))))
(Nil)
((one,1) +: ((two,2) +: ((three,3) +: (Nil))))

head +: tail은 어떤 Seq의 머리 원소와 꼬리 Seq(나머지 시퀀스)를 일치시킨다. 연산자 +:는 시퀀스의 '콘즈(cons)' 연산자이다. List의 경우에는 ::가 사용되는데, scala 2.11부터는 Seq 전체에 적용되는 +:를 더 많이 사용한다. 이 경우에 매칭이 되는 규칙은 다음과 같다.

  • 비어있지 않은 시퀀스에만 일치하고, 머리와 꼬리 Seq를 head와 tail이라는 이름의 변경 불가능한 변수에 추출함
  • head와 tail은 임의의 변수이름임

비어있는 시퀀스의 경우에는 Nil에 매칭이 되게 된다.

위의 출력을 복사한 다음에 붙여 넣으면, 원래 객체를 재구성할 수 있다. 즉, +: 혹은 ::를 사용한 생성(construction)과 패턴매칭/분해(deconstruction) 사이에는 대칭성이 존재한다.

scala> val s1 = (1 +: (2 +: (3 +: (4 +: (5 +: (Nil))))))
s1: List[Int] = List(1, 2, 3, 4, 5)
scala> val s2 = (("one", 1) +: (("two", 2) +: (("three", 3) +: (Nil))))
s2: List[(String, Int)] = List((one,1), (two,2), (three,3))
scala> val m = Map(s2 :_*)
m: scala.collection.immutable.Map[String,Int] = Map(one -> 1, two -> 2, three -> 3)

4.4 튜플에 일치시키기

튜플의 경우에도 유사하게 사용할 수 있다. 튜플의 특성 item값만 일치시키고 싶을 때는, 상관없는 item의 위치에 _를 사용하면 된다.

val langs = Seq(
  ("Scala",   "Martin", "Odersky"),
  ("Clojure", "Rich",   "Hickey"),
  ("Lisp",    "John",   "McCarthy"))

for (tuple <- langs) {
  tuple match {
    case ("Scala", _, _) => println("Found Scala")                   // <1>
    case (lang, first, last) =>                                      // <2>
      println(s"Found other language: $lang ($first, $last)")
  }
}

4.5 케이스 절의 가드

for 내장에서와 마찬가지로 가드를 사용할 수 있음. 아래에서 <1>은 짝수인 경우에, <2>는 그 이외의 경우에 수행이 된다.

for (i <- Seq(1,2,3,4)) {
  i match {
    case _ if i%2 == 0 => println(s"even: $i")                       // <1>
    case _             => println(s"odd:  $i")                       // <2>
  }
}
Clone this wiki locally