276
2
부
기본기 다지기
바’로 채택하게 만드는 것이다. 이를 위해 약간 이상하고 악마적인 함수형 기능을 가능한 한 피
해야 한다. 하지만 나는 여러분이 그런 부분의 아름다움과 강력함을 인정하게 되길 바라며, 이
런 기능을 명백히 드러낼 것이다.
이번 장에서는 새로운 스칼라 개발자라면 반드시 알아두어야 할 필수적인 요소를 설명할 것이
다. 함수형 프로그래밍은 넓고 다양한 분야다. 따라서 새로운 개발자에게 덜 필수적인 좀 더 어
려운 주제는
16
장에서 다룰 것이다.
6.2
스칼라 함수형 프로그래밍
스칼라는 복합적인 객체
-
함수형 언어이기 때문에, 함수가 순수하도록 요구하지 않으며, 변수가
불변일 것을 요구하지도 않는다. 하지만 가능하면 그런 방식으로 코드를 작성하도록 장려한다.
우리가 이미 살펴본 것을 빠르게 한 번 정리해보자.
정수의 리스트를 반복하면서 짝수만 골라서
2
배한 다음,
reduce
를 사용해서 그들을 서로 곱하
고 축약하기 위해 몇 가지 고차 함수를 사용했다.
//
src
/
main
/
scala
/
progscala2
/
fp
/
basics
/
hofs
-
example
.
sc
(
1 to 10
)
filter
(
_
%
2
==
0
)
map
(
_
*
2
)
reduce
(
_
*
_
)
결과는
122880
이다.
_ %
2
==
0
, _ *
2
, _ * _
가 함수 리터럴이라는 사실을 기억하라. 앞의 두 함수는 위치지정자
_
에 할당된 인자를 취한다.
reduce
에 전달된 마지막 함수는 인자를 두 개 취한다.
reduce
함수는 여러 원소를 서로 곱하기 위해 사용되었다. 즉, 그 함수는 정수의 컬렉션을 단일
값으로 ‘축약
reduce
’한다.
reduce
에 전달한 함수는 인자를 둘 취하고, 각 인자는 _ 위치지정자에
할당된다. 인자 중 하나는 입력 컬렉션의 현재 원소고, 다른 하나는 ‘누적값’이다. 이 누적값은
최초 호출 시에는 컬렉션의 원소지만, 그다음부터는 직전에
reduce
를 호출한 결과로 얻은 값
이다 (누적값이 인자의 첫 번째 값일지 두 번째 값일지는 구현에 따라 달라진다 ).
reduce
에 전
277
6
장
스칼라 함수형 프로그래밍
달할 함수에는 수행할 축약 연산의 결합 법칙이 성립해야 한다는 요구 조건이 있다. 곱셈이나 덧
셈이 바로 그런 연산이다. 결합 법칙이 필요한 이유는 컬렉션 원소를 어떤 순서로 처리할지 아무
런 보장이 없기 때문이다!
따라서 반복 횟수를 추적하기 위한
var
변수나 축약 중인 값을 갱신할
var
변수를 사용하지 않
으면서도 성공적으로 모든 리스트의 원소에 대해 ‘루프’를 돌 수 있었다.
6.2.1
익명 함수, 람다, 클로저
앞의 예제를 다음과 같이 바꿔보자.
//
src
/
main
/
scala
/
progscala2
/
fp
/
basics
/
hofs
-
closure
-
example
.
sc
var factor
=
2
val multiplier
=
(
i
:
Int
)
=
>
i
*
factor
(
1 to 10
)
filter
(
_
%
2
==
0
)
map multiplier reduce
(
_
*
_
)
factor
=
3
(
1 to 10
)
filter
(
_
%
2
==
0
)
map multiplier reduce
(
_
*
_
)
먼저
factor
라는 변수를 만들어서 승수로 활용한다. 또한 앞의 예제에 있는 익명 함수
_ *
2
를
분리해서
factor
를 사용하는
multiplier
라는 값에 저장한다.
multiplier
가 함수라는 사실에
유의하라. 스칼라에서는 함수도
1
급 계층값이기 때문에, 함수를 일반적인 값과 마찬가지로 정의
할 수 있다. 하지만
multiplier
는 하드코딩된
2
라는 값 대신
factor
를 참조한다.
factor
의 값을 변경하면서 같은 컬렉션에 대해 같은 코드를 두 번 실행한다. 첫 실행은 예전과
마찬가지로
122880
이라는 값을 얻지만, 두 번째 실행에서는
933120
을 얻는다.
비록
multiplier
가 변경 불가능한 함수값이었지만,
factor
가 바뀜에 따라 그 함수의 동작도 바
뀌었다.
multiplie
r
에는 두 가지 변수
i
와
factor
가 들어 있다.
i
는 함수의 형식 인자
formal
parameter
다.
즉, 그 값은
multiplier
가 호출될 때마다 새로운 값에 결부
bind
된다. 하지만
factor
는 형식 인
자가 아니며, 자유 변수
free
variable
, 즉 주위를 둘러싼 영역에 있는 변수에 대한 참조다. 따라서
278
2
부
기본기 다지기
컴파일러는
multiplier
안의 문맥과
multiplier
안에서 지정되지 않은 변수들이 참조하는
외부 문맥을 함께 아우르는(또는 환경에 대해 ‘닫혀 있는’ ) 클로저
closure
를 만든다. 따라서 클로
저는
multiplier
내의 모든 변수를 지정해준다.
이런 이유로
factor
가 바뀌면
multiplier
의 동작도 바뀐다.
multiplier
는
factor
를 참조하
며, 계산을 수행할 때마다
factor
의 현재 값을 가져온다. 어떤 함수에 아무런 외부 참조도 없
다면, 그냥 그 자신만으로도 이미 닫혀 있는 함수다. 이런 경우에는 외부 문맥이 필요 없다.
심지어
Factor
가 메서드 등 어떤 영역에 속한 지역 변수였거나, 다른 영역으로
multiplier
를
전달하는 경우에도 잘 작동할 것이다.
Multiplier
를 전달하면, 그 안의 자유 변수
factor
도 함
께 전달된다.
//
src
/
main
/
scala
/
progscala2
/
fp
/
basics
/
hofs
-
closure2
-
example
.
sc
def m1
(
multiplier
:
Int
=
>
Int
)
=
{
(
1 to 10
)
filter
(
_
%
2
==
0
)
map multiplier reduce
(
_
*
_
)
}
def m2
:
Int
=
>
Int
=
{
val factor
=
2
val multiplier
=
(
i
:
Int
)
=
>
i
*
factor
multiplier
}
m1
(
m2
)
m2
를 호출해서
Int
=
>
Int
타입의 함수값을 반환받는다.
m2
는 내부의
multiplier
라는 값을
반환한다. 하지만
m2
에는
factor
라는 다른 값도 들어 있다. 이 값은
m2
에서 반환된 다음에는
영역의 밖에 있다.
그 후
m1
을 호출하면서
m2
가 반환한 함수값을 넘긴다. 여기서 비록
factor
가
m1
내부의 영역
에는 없지만, 출력은 예전과 마찬가지로
122880
이다.
m2
가 반환한 함수는 실제로는
factor
에
대한 참조까지 포함하는 클로저다.
개념이 약간 중복되는 자주 사용되는 용어가 몇 가지 있다.
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.