184
2
기본기 다지기
println
(
str
)
}
이제 번째
case
절은
Int
Double
값이 일치한다.
4.3
시퀀스에 일치시키기
Seq
(
http
://
bit
.
ly
/
1wQxJyd
)정해진 순서대로 원소를 순회할 있는
List
(
http
://
bit
.
ly
/
1toub3N
)
Vector
(
http
://
bit
.
ly
/
1tozAI0
) 등의 모든 구체적인 컬렉션 타입의 부모 타입
이다 (
Seq
시퀀스
sequence
(‘순서가 정해진 이라는 )에서 말이다 ).
패턴 매칭과 재귀를 사용해서
Seq
순회하는 전통적인 관용구를 살펴보면서, 시퀀스에 대해
가지 유용한 기본 사항을 배우자.
//
src
/
main
/
scala
/
progscala2
/
patternmatching
/
match
-
seq
.
sc
val nonEmptySeq
=
Seq
(
1
,
2
,
3
,
4
,
5
)
//
val emptySeq
=
Seq
.
empty
[
Int
]
val nonEmptyList
=
List
(
1
,
2
,
3
,
4
,
5
)
//
val emptyList
=
Nil
val nonEmptyVector
=
Vector
(
1
,
2
,
3
,
4
,
5
)
//
val emptyVector
=
Vector
.
empty
[
Int
]
val nonEmptyMap
=
Map
("
one
"
-
>
1
,
"
two
"
-
>
2
,
"
three
"
-
>
3
)
//
val emptyMap
=
Map
.
empty
[
String
,
Int
]
def seqToString
[
T
](
seq
:
Seq
[
T
]):
String
=
seq match
{
//
case head
+:
tail
=
>
s
"$
head
+:
"
+
seqToString
(
tail
)
//
case Nil
=
>
"
Nil
"
//
}
for
(
seq
<
-
Seq
(
//
nonEmptySeq
,
emptySeq
,
nonEmptyList
,
emptyList
,
nonEmptyVector
,
emptyVector
,
nonEmptyMap
.
toSeq
,
emptyMap
.
toSeq
))
{
println
(
seqToString
(
seq
))
}
185
4
패턴 매칭
비어 있지 않은
Seq
[
Int
]
(
http
://
bit
.
ly
/
1E8xLCt
)만든다(실제로는
List
(
http
://
bit
.
ly
/
15iqGNE
)반환된다). 그다음 줄은 비어 있는
Seq
[
Int
]
만드는 전형적인 방법을 보여
준다.
➋ 비어 있지 않은
List
[
Int
]
(
Seq
의 서브타입 )를 만든다. 그다음 줄은 라이브러리에서 모든
타입 매개변수에 대해 비어 있는
List
표현하는 객체인
Nil
선언한다.
비어 있지 않은
Vectors
[
Int
]
(
http
://
bit
.
ly
/
1bgKyXi
)만든다(이 타입도
Seq
서브
타입이다 ). 그다음 줄은 비어 있는
Vector
[
Int
]
만든다.
➍ 비어 있지 않은
Map
[
String
,
Int
]
(
http
://
bit
.
ly
/
13MzP5e
)를 만든다.
Map
Seq
의 서브
타입이 아니다. 이에 대해서는 잠시 후에 설명할 것이다.
key
String
을 사용하고, 값으로
Int
사용한다. 그다음 줄은 비어 있는
Map
[
String
,
Int
]
만든다.
T
라는 타입에 대해
Seq
[
T
]
로부터
String
생성하는 재귀적 메서드를 정의한다. 본문은
력으로 들어온
Seq
[
T
]
대해 매치하는
4
줄짜리 식이다.
➏ 매치 절이 둘 있고, 이 둘이 모든 경우를 다 처리한다. 첫 번째 매치는 모든 비어 있지 않은
Seq
와 일치하며, 첫 원소인 머리(
head
)와 꼬리 (
tail
) (
Seq
에서 머리를 제외한 나머지 부분)
를 가져온다 (
Seq
에도
head
tail
이라는 메서드가 있다. 하지만 여기서는 일반적인
case
에서 사용하는 변수 이름으로 단어를 사용했다).
case
절의 본문에서는 머리 다음에
+:
,
그리고 그다음에
seqToString
꼬리에 대해 호출한 결과를 넣어서
String
만들어낸다.
다른 유일한 경우는
Seq
다.
List
표현하는 특별 객체 (
Nil
)사용해서 모든 리스
트와 일치시킬 수 있다. 모든
Seq
는 항상 같은 타입의 빈 인스턴스로 끝나는 것처럼 해석된다
는 사실을 기억하라. 하지만 실제로는
List
와 같은 몇몇 타입만 실제로시퀀스의 인스턴스
구현한다.
Seq
다른
Seq
안에 넣는다 (
Map
toSeq
호출하면
-
쌍의 시퀀스로 맵을 바꿀
). 그 후 그
Seq
를 순회하면서 각각의 원소
Seq
에 대해
seqToString
을 호출한 결과를 출력
한다.
다음은 출력이다.
186
2
기본기 다지기
1
+:
2
+:
3
+:
4
+:
5
+:
Nil
Nil
1
+:
2
+:
3
+:
4
+:
5
+:
Nil
Nil
1
+:
2
+:
3
+:
4
+:
5
+:
Nil
Nil
(
one
,
1
)
+:
(
two
,
2
)
+:
(
three
,
3
)
+:
Nil
Nil
Map
순회 특별한 순서를 보장하지 않기 때문에
Seq
서브타입이 아니다. 따라서
Map
.
toSeq
호출해서 키
-
튜플의 시퀀스를 만들었다. 이렇게 만든
Seq
여전히 삽입 순서대로
-
쌍을 보여준다. 이는 작은
Map
구현에 따라 생긴 부수적 효과지 보장되는 성질은
니다. 여러 컬렉션을 출력한 결과를 보면
seqToString
작동했음을 있다.
두 가지 새로운 유형의
case
절이 있다. 먼저
head
+:
tail
은 어떤
Seq
의 머리 원소와 꼬리
Seq
(나머지 시퀀스 )를 일치시킨다. 연산자
+:
시퀀스의 ‘콘즈’ 연산자다. 이는
3
.
3
‘우선순
규칙에서
List
::
연산자와 비슷하다. 메서드 이름이 콜론 (
:
)으로 끝나면 오른쪽으로
결합되어
Seq
꼬리에 대한 호출이 됨을 기억하라.
이들을 연산자’나 메서드’라고 부르고 있지만, 문맥에서 그런 용어는 전혀 맞지 않다.
여기서 정말로 어떤 일이 벌어지고 있는지에 대해서는 잠시 후에 다시 설명할 것이다. 지금은
가지 핵심적인 부분을 짚어보자.
첫째, 이
case
절은 오직 비어 있지 않은 시퀀스와만 일치한다. 최소한 머리 원소는 있어야
며, 패턴은 머리 원소와 시퀀스의 나머지 부분을 각각
head
tail
이라는 이름의 변경 불가
능한 변수에 추출해준다.
둘째, 다시 일러두지만
head
tail
임의의 변수 이름이다. 하지만
Seq
에는 시퀀스의 머리와
꼬리를 돌려주는
head
tail
메서드도 있다. 보통은 메서드를 사용하는 중인지 여부를 문맥
에서 명확히 있다. 한편 시퀀스에 대해 메서드를 호출하면 예외가 발생한다.
Seq
가 개념적으로는 연결 리스트처럼 동작한다. 연결 리스트에서 각각의 머리 노드는 원소를
저장하고, 꼬리 (시퀀스의 나머지 )를 가리킨다. 노드가
4
개인 시퀀스를 표현하면 아래와 같은
계층구조를 가진다. 따라서 표현하기 위해 가장 자연스러운 표지
marker
사용할 있는
것은 바로 시퀀스다.

Get 프로그래밍 스칼라: 실용적인 스칼라 활용법을 익히는 가장 확실한 실전 바이블 (2.11.x 버전 기반) now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.