web/ko/pattern-matching-and-functional-composition.textile (98 lines of code) (raw):
---
prev: collections.textile
next: type-basics.textile
title: 패턴 매치와 함수 합성
layout: post
---
이번 강좌에서 다루는 내용은 다음과 같다.
* "함수 합성":#composition
** compose
** andThen
* "커링과 부분 적용 비교":#curryvspartial
* "PartialFunction(부분 함수 클래스)":#PartialFunction
** 치역과 정의역
** orElse로 합성하기
* "case 문이란 무엇인가?":#case
h2(#composition). 함수 합성
다음 두 함수가 유용하다고 가정하자.
<pre>
scala> def addUmm(x: String) = x + " umm"
addUmm: (x: String)String
scala> def addAhem(x: String) = x + " ahem"
addAhem: (x: String)String
</pre>
h3. compose
<code>f compose g</code>를 하면 두 함수를 <code>f(g(x))</code>과 같이 합성한다.
<pre>
scala> val ummThenAhem = addAhem _ compose addUmm _
ummThenAhem: (String) => String = <function1>
scala> ummThenAhem("well")
res0: String = well umm ahem
</pre>
h3. andThen
<code>andThen</code>도 <code>compose</code>와 비슷하지만, 처음 오는 함수를 먼저 호출한 다음, 두번째 함수를 호출한다. 즉, <code>f andThen g</code>는 <code>g(f(x))</code>
<pre>
scala> val ahemThenUmm = addAhem _ andThen addUmm _
ahemThenUmm: (String) => String = <function1>
scala> ahemThenUmm("well")
res1: String = well ahem umm
</pre>
h2(#curryvspartial). 커링과 부분 적용의 비교
h3. 케이스 문
h4. 케이스 문은 도대체 무엇인가?
케이스 문은 PartialFunction이라 불리는 함수의 하위 클래스이다.
h4. 케이스 문을 여러개 사용하는 것은 무엇인가?
여러 PartialFunction들이 서로 compose된 것이다.
h2(#PartialFunction). PartialFunction
함수는 정의된 모든 인자 값에 대해 동작한다. 다른식으로 말하자면, (Int) => String 타입으로 정의된 함수는 모든 Int에 대해 어떤 String을 반환한다. 즉, 어떤 정수이건 그에 대응하는 String이 있기 마련이다.
하지만 부분 함수는 인자 타입의 값 중 일부에 대해서만 정의되어 있다. (Int) => String 타입의 부분함수는 일부 Int는 취급하지 않을 것이다.
<code>isDefinedAt</code>은 PartialFunction에 정의되어 있는 메소드로, 해당 PartialFunction이 주어진 인자를 받을 수 있는지를 알려준다.
__Note__ <code>PartialFunction</code>은 앞에서 본 부분 적용된 함수와는 전혀 관계가 없다.
*See Also* 효율적 스칼라에도 <a href="https://twitter.github.com/effectivescala/#Functional programming-Partial functions">부분함수</a>에 대한 내용이 있다.
<pre>
scala> val one: PartialFunction[Int, String] = { case 1 => "one" }
one: PartialFunction[Int,String] = <function1>
scala> one.isDefinedAt(1)
res0: Boolean = true
scala> one.isDefinedAt(2)
res1: Boolean = false
</pre>
부분 함수도 함수처럼 적용이 가능하다.
<pre>
scala> one(1)
res2: String = one
</pre>
PartialFunction들은 orElse라 불리는 다른 함수를 사용해 합성이 가능하다. orElse는 각 PartialFunction이 인자 값에 대해 정의되어 있는지 여부에 따라 적절한 최종 값을 반환한다.
<pre>
scala> val two: PartialFunction[Int, String] = { case 2 => "two" }
two: PartialFunction[Int,String] = <function1>
scala> val three: PartialFunction[Int, String] = { case 3 => "three" }
three: PartialFunction[Int,String] = <function1>
scala> val wildcard: PartialFunction[Int, String] = { case _ => "something else" }
wildcard: PartialFunction[Int,String] = <function1>
scala> val partial = one orElse two orElse three orElse wildcard
partial: PartialFunction[Int,String] = <function1>
scala> partial(5)
res24: String = something else
scala> partial(3)
res25: String = three
scala> partial(2)
res26: String = two
scala> partial(1)
res27: String = one
scala> partial(0)
res28: String = something else
</pre>
h3(#case). 케이스 문의 신비
지난 강의에서 무언가 이상한 부분이 있었다. 즉, 케이스 문을 함수가 필요한 위치에 사용하는 것을 보았다.
<pre>
scala> case class PhoneExt(name: String, ext: Int)
defined class PhoneExt
scala> val extensions = List(PhoneExt("steve", 100), PhoneExt("robey", 200))
extensions: List[PhoneExt] = List(PhoneExt(steve,100), PhoneExt(robey,200))
scala> extensions.filter { case PhoneExt(name, extension) => extension < 200 }
res0: List[PhoneExt] = List(PhoneExt(steve,100))
</pre>
어떻게 이런 일이 가능할까?
필터는 함수를 받는다. 위 코드에서 필터가 받아야 하는 함수는 (PhoneExt) => Boolean 타입이다.
PartialFunction은 Function의 하위타입이므로 필터는 PartialFunction도 받을 수 있다!