-
Notifications
You must be signed in to change notification settings - Fork 7
C/C++/Java의 switch/case
문과 유사하나, 더 다양한 기능을 제공함
val bools = Seq(true, false)
for (bool <- bools) {
bool match {
case true => println("Got heads")
case false => println("Got tails")
}
}
bool
의 값이 true
, false
임에 따라서, 각각의 case
에 맞는 명령이 실행됨
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내의 변수값을 참고하기 싶을 때는 변수 이름 앞뒤에 ``를 붙여야 한다.
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)
튜플의 경우에도 유사하게 사용할 수 있다. 튜플의 특성 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)")
}
}
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>
}
}