287
6
장
스칼라 함수형 프로그래밍
scala
>
val inverse
:
PartialFunction
[
Double
,
Double
]
=
{
|
case d if d
!
=
0
.
0
=
>
1
.
0
/
d
|
}
inverse
:
PartialFunction
[
Double
,
Double
]
=
<
function1
>
scala
>
inverse
(
1
.
0
)
res128
:
Double
=
1
.
0
scala
>
inverse
(
2
.
0
)
res129
:
Double
=
0
.
5
scala
>
inverse
(
0
.
0
)
scala
.
MatchError
:
0
.
0
(
of class java
.
lang
.
Double
)
at scala
.
PartialFunction
$$
anon
$
1
.
apply
(
PartialFunction
.
scala
:
248
)
at scala
.
PartialFunction
$$
anon
$
1
.
apply
(
PartialFunction
.
scala
:
246
)
...
이는 그렇게 강건한 ‘역수’ 함수는 아니다. 왜냐하면
0
은 아니지만 매우 작은
d
의 경우
1
/
d
의 값
이 잘못될 수 있기 때문이다! 물론 진짜 핵심은
Double
의 경우
0
.
0
이 아닌 경우에만 역수가 존
재한다는 사실이다.
NOTE
_
부분 적용 함수는 함수의 인자 목록을 일부 적용하지만, 전부 적용(또는 제공)하지 않은 식을 말한
다. 이 식은 나머지 인자 목록을 취하는 새로운 함수를 반환한다. 부분 함수는 인자가 하나뿐인 함수로, 인자
의 타입이 취할 수 있는 값 중 일부에 대해서만 정의된 함수다. 부분 함수의 리터럴 문법은 하나 이상의
case
절을 중괄호로 둘러싼 것이다.
6.6
함수의 커링과 다른 변환
2
.
5
.
2
절 ‘인자 목록이 여럿 있는 메서드’에서 메서드에 여러 인자 목록이 있는 것이 무슨 뜻인
지 배웠다. 이런 여러 인자 목록의 실용적인 예에 대해서도 설명했다. 하지만 이런 개념을 지원
하는 함수의 기본적 특성이 있는데, 이를 커링
currying
이라고 부른다. 이 이름은 수학자인 하스켈
커리
Haskell
Curry
를 기린 것이다(하스켈이라는 언어 이름도 마찬가지다). 실제로 하스켈의 연구
288
2
부
기본기 다지기
는 원래 모제스 스켄핑켈
Moses
Schönfinkel
의 생각에 기반한 것이지만, 스켄핑켈링
Schönfinkeling
이나
스켄핑켈화
Schönfinkelization
라고 말하는 것은 아무래도 어렵기 때문에 커링이란 용어를 사용한다.
커링 변환은 여러 인자를 취하는 함수를 단 하나의 인자를 취하는 여러 함수의 연쇄로 바꿔준다.
스칼라에서, 커링한 함수는 여러 인자 목록으로 정의된다. 이때 각 인자 목록에는 인자가 하나씩
들어간다. 앞 절에서 살펴본
cat1
메서드를 떠올려보자.
//
src
/
main
/
scala
/
progscala2
/
fp
/
datastructs
/
curried
-
func
.
sc
def cat1
(
s1
:
String
)(
s2
:
String
)
=
s1
+
s2
커링한 함수를 정의하기 위해 다음과 같은 구문을 사용할 수도 있다.
def cat2(s1: String) = (s2: String) => s1 + s2
첫 번째 구문이 더 읽기 쉽지만, 두 번째 방식을 사용하면 커링한 함수를 부분 적용하는 경우 뒤
에 밑줄을 추가할 필요가 없다.
scala
>
def cat2
(
s1
:
String
)
=
(
s2
:
String
)
=
>
s1
+
s2
cat2
:
(
s1
:
String
)
String
=
>
String
scala
>
val cat2hello
=
cat2
("
Hello
")
//
_
가
없음
cat2hello
:
String
=
>
String
=
<
function1
>
scala
>
cat2hello
("
World
!")
res0
:
String
=
Hello World
!
두 함수를 호출하는 것은 동일하며, 결과도 같다.
scala
>
cat1
("
foo
")("
bar
")
res0
:
String
=
foobar
289
6
장
스칼라 함수형 프로그래밍
scala
>
cat2
("
foo
")("
bar
")
res1
:
String
=
foobar
또한
curried
메서드를 호출하면 여러 인자를 취하는 메서드를 커링한 형태로 바꿀 수 있다
(
curried
를 호출하기 위해 부분 적용 구문을 어떻게 활용하는지 살펴보라 ).
scala
>
def cat3
(
s1
:
String
,
s2
:
String
)
=
s1
+
s2
cat3
:
(
s1
:
String
,
s2
:
String
)
String
scala
>
cat3
("
hello
",
"
world
")
res2
:
String
=
helloworld
scala
>
val cat3Curried
=
(
cat3
_
).
curried
cat3Curried
:
String
=
>
(
String
=
>
String
)
=
<
function1
>
scala
>
cat3Curried
("
hello
")("
world
")
res3
:
String
=
helloworld
이 예제에서 우리는 인자를 둘 받는 함수
cat3
를 여러 인자 목록을 받는 동등한 커링한 함수로
변환했다. 만약
cat3
가 인자를
3
개 받는 함수였다면, 커링한 버전도 인자 목록을
3
개 받을 것
이며, 그 이상의 인자를 받는 경우에도 마찬가지로 인자 목록 개수가 늘어날 것이다.
cat3Curried
의 타입 시그니처인
String
=
>
(
String
=
>
String
)
에 유의하라. 이에 대해 좀
더 자세히 살펴보자. 이번에는 함수값을 사용한다.
//
src
/
main
/
scala
/
progscala2
/
fp
/
datastructs
/
curried
-
func2
.
sc
scala
>
val f1
:
String
=
>
String
=
>
String
=
(
s1
:
String
)
=
>
(
s2
:
String
)
=
>
s1
+
s2
f1
:
String
=
>
(
String
=
>
String
)
=
<
function1
>
scala
>
val f2
:
String
=
>
(
String
=
>
String
)
=
(
s1
:
String
)
=
>
(
s2
:
String
)
=
>
s1
+
s2
f2
:
String
=
>
(
String
=
>
String
)
=
<
function1
>
scala
>
f1
("
hello
")("
world
")
res4
:
String
=
helloworld
290
2
부
기본기 다지기
scala
>
f2
("
hello
")("
world
")
res5
:
String
=
helloworld
타입 시그니처
String
=
>
String
=
>
String
은
String
=
>
(
String
=
>
String
)
과 동일하다.
f1
이나
f2
에 인자를 하나만 넘기면, 첫 번째 인자를 결부시킨 다음
String
=
>
String
타입의
새로운 함수를 반환한다.
Function
(
http
://
bit
.
ly
/
1zmu8ce
) 객체에 들어 있는 메서드를 사용해서 함수를 ‘언커링’할
수도 있다.
scala
>
val cat3Uncurried
=
Function
.
uncurried
(
cat3Curried
)
cat3Uncurried
:
(
String
,
String
)
=
>
String
=
<
function2
>
scala
>
cat3Uncurried
("
hello
",
"
world
")
res6
:
String
=
helloworld
scala
>
val ff1
=
Function
.
uncurried
(
f1
)
ff1
:
(
String
,
String
)
=
>
String
=
<
function2
>
scala
>
ff1
("
hello
",
"
world
")
res7
:
String
=
helloworld
커링을 실용적으로 사용하는 예로는 함수를 특정 데이터에 대해 특화시키는 것을 들 수 있다.
먼저 아주 일반적인 경우로 시작해서, 커링을 통해 특별한 경우로 함수를 좁혀 나갈 수 있다.
이런 접근 방식의 간단한 예를 보자. 다음 코드는 곱셈을 수행하는 기반 함수의 특별한 형태를
보여준다.
scala
>
def multiplier
(
i
:
Int
)(
factor
:
Int
)
=
i
*
factor
multiplier
:
(
i
:
Int
)(
factor
:
Int
)
Int
scala
>
val byFive
=
multiplier
(
5
)
_
byFive
:
Int
=
>
Int
=
<
function1
>
scala
>
val byTen
=
multiplier
(
10
)
_
byTen
:
Int
=
>
Int
=
<
function1
>
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.