스칼라100(14) Function type

154 views
Skip to first unread message

최정열

unread,
Jan 20, 2014, 1:12:01 PM1/20/14
to scala...@googlegroups.com

Fuction type

다음 코드에서s의 타입은 String입니다.

val s = "Hello"

valvardef로 이름을 선언하고, 그 뒤에:를 붙여, 다음처럼 타입을 지정 할 수 있습니다.

val s: String = "Hello"

다음 ls의 타입은 List[Int] 입니다.

val ls: List[Int] = List(1, 2, 3)

다음은 문자열을 파라미터로 받아서, 아무 것도 하지 않고 그대로 리턴하는 메소입니다.

def f(s: String) = s

메소드는 함수형 프로그래밍의 함수와는 차이가 있습니다. 그래서 함수의 타입에 대해서 설명하기 위해서 위 메소드를 다음처럼 함수 리터럴로 변환시켜 보겠습니다.

val f = (s: String) => s

메소드 표현 보다는 약간 익숙하지 않습니다. 하지만 어떻게 바뀌는지 잘 눈여겨 보시길 바랍니다.

함수에도 타입이 있다는 개념이 생소할지 모르지만, 알고보면 간단합니다. (파라미터 타입 =>리턴 타입)으로 함수의 타입이 결정됩니다.

스칼라는 정적타입언어이고, 함수 역시 다른 값과 마찬가지로 타입이 존재합니다. 위 함수는 String을 인자로 받고 String을 내놓으니 String => String이 타입이 되는 것이지요.

그래서, 함수 표현서 이름 뒤에:를 추가하고 그 뒤에, 명시적으로 타입을 나타내는 부분(String => String)만 추가하면 다음과 같습니다. 이전에 생략이 가능했던 이유는 스칼라의 타입추론(type inference) 때문입니다.

val f: (String => String) =  (s: String) => s

위 코드 처럼 val f타입을 명시적으로 지정해 준 경우도 마찬가지로, 타입 추론으로 인하여 함수의 파라미터에 해당하는(s: String)의 타입 부분을 생략 할 수 있습니다. 익숙해지기 전 까지는 이렇게 다르게 표현되는 점 때문에 함수리터럴을 찬찬히 살펴봐야 파악 할 수 있습니다.

val f: (String => String) = s => s

String => String의 타입은 그 자체로 존재하는 대신, 내부적으로Function1[String, String]이라는 타입으로 표현됩니다. ScalaIDE의 worksheet이나 Repl을 활용하면 String => String = <function1>라는 출력을 보실 수 있습니다.

Function1[String, String]에서 Function뒤의 1이라는 숫자는 파라미터의 갯수를 나타냅니다. 그리고 앞의 String은 함수가 전달 받는 인자의 타입입니다. 그리고 마지막의 String은 리턴 타입니다.

이번에는 Int를 입력 받고 String을 리턴하는 함수를 보겠습니다.

val f =  (i: Int) => i.toString

위의 함수 타입은 (Int => String) 타입이겠죠. 함수의 파라미터에서 Int형을 전달 받고 String으로 변환해서 리턴하니까요. 마찬가지로 내부적으로는 Function1[Int, String] 타입으로 표현됩니다.

val f = (s1: String, s2: String) => s1 + ", " + s2

위 코드 처럼 파라미터를 두 개 받는 함수의 타입은 (String, String) => String이 되겠죠. 그리고, 파라미터의 타입이 두 개이므로 내부적으로는 Function2로 표현됩니다. 좀 더 자세히 보면Function2[String, String, String] 처럼 표현됩니다. 첫 번재와 두 번째의 String은 각각 함수의 첫 번째, 두 번째 파라미터의 타입이며, 마지막String은 리턴값의 타입을 나타냅니다.

그럼, 실제 위와 같은 코드를 Function2타입을 사용해서 정의해보겠습니다.

val f2 = new Function2[String, String, String] {
  def apply(a: String, b: String) = {
    a + ", " + b
  }
}
f2("Foo", "Bar")    //> Foo, Bar

일반적으로 익명클래스를 정의하는 것과 거의 비슷하죠. 실제로도 함수리터럴을 정의하면 익명클래스를 정의하는 것과 거의 가은 방식으로 작동합니다. 그리고 def apply부분이 보이는데요. 함수의 이름과 ()만 사용해서 파라미터를 전달하면, 알아서 apply부분이 호출됩니다. 위와 같이 직접 Function2를 사용해서 함수를 선언 할 일은 없지만, apply로 전달되는 메커니즘은 기억해 두세요.


Scala100 Home


Hyunsok Oh

unread,
Jan 20, 2014, 4:11:35 PM1/20/14
to scala...@googlegroups.com
몇가지 (아마 앞으로 다루셔야 할) 내용을 말씀드리면..

1) (TypeA, TypeB) => TypeC (즉, Function2[TypeA, TypeB, TypeC])는
((TypeA,TypeB))=>TypeC (즉, Function1[(TypeA, TypeB),TypeC])와 다르다는
점을 코드를 읽을 때 혼동하면 안됩니다.

이를 서로 변환하는 Function.tupled와 Function.untupled가 있습니다.

2) Function2등을 필요하면 가끔 상속해서 쓰기도 합니다. 전형적인 예중 하나는 다음과 같이 함수를 받아 메모화된
함수를 반환하는 경우죠.

// case class Memo[A, B](f: A => B) extends Function1[A,B] 와 같음
case class Memo[A, B](f: A => B) extends (A => B) {
private val cache = mutable.Map.empty[A, B]
def apply(x: A) = cache getOrElseUpdate (x, f(x))
}

val facMem = Memoize1(fac) // fac은 팩토리얼 함수

데코레이터 패턴을 아시는 분이라면 함수의 데코레이터를 만든다고 설명할 수 있습니다.

3) 앞으로 당연히 커링과 언커링에 대해서는 다루실 것 같고..

4) 메소드와 함수의 차이에 대해서도 다루실 것 같고...

-------------
오자 신고:
첫 번재와 두 번째의 String은 각각 함수의 => 첫 번"째"와

익명클래스를 정의하는 것과 거의 가은 방식으로 => 거의 "같은" 방식으로 ^^

매번 감사드립니다.



2014/1/21 최정열 <mye...@gmail.com>:
> --
> Google 그룹스 '라 스칼라 코딩단' 그룹에 가입했으므로 본 메일이 전송되었습니다.
> 이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 scala-korea...@googlegroups.com에 이메일을
> 보내세요.
> 이 그룹에 게시하려면 scala...@googlegroups.com(으)로 이메일을 보내세요.
> 웹에서 이 토론을 보려면
> https://groups.google.com/d/msgid/scala-korea/d6fc3e11-cf5e-4677-af26-76b127c93bd4%40googlegroups.com
> 을(를) 방문하세요.
> 더 많은 옵션을 보려면 https://groups.google.com/groups/opt_out을(를) 방문하세요.

최정열

unread,
Jan 20, 2014, 11:21:57 PM1/20/14
to scala...@googlegroups.com

앗 감사합니다.

Function1[ (TypeA, TypeB), TypeC ] 는 Function1[ Tuple2[TypeA, Typeb], TypeC ]

로 표현됩니다. 저도 처음에 자주 헷갈렸던 것인데, 꼭 짚어주셨네요.

Function.tupled는 이야기 했었는데, untupled가 있는건 지금 처음 알았습니다.

def add(x: (Int, Int)) = {
  x._1 + x._2
}

val add2 = Function.untupled(add _)

해보니까 되네요~ ㅎㅎ

add( x: (Int, Int) )처럼 튜플을 인자로 받는 경우는 원래 add((1, 2)) 로 작성해야 하지만, add(1, 2)처럼 해 줘도 자동으로 튜플링해서 전달해주는 부분도 생각났네요.

2) 번의 경우도, (A => B) 를 상속받는게, Function1을 상속받는 것이고, 데코레이터패턴을 예로 들어주시니, 이해하기 쉬운 것 같습니다.

앞으로도 틈틈히 첨언 부탁드리겠습니다~ 감사합니다~ ^ㅡ^


Hyunsok Oh

unread,
Jan 21, 2014, 12:02:43 AM1/21/14
to scala...@googlegroups.com
메모이제이션 예제에서 Memoize1가 이나고 Memo네요 ^^;;

한가지, 저는 A=>B 라는 타입을 Function1[A,B]보다 더 좋아합니다. (A,B)를 Tuple2[A,B] 보다 좋아하고요.

Function1, Tuple2 등을 사용하지 않는 편이 좋다고 생각하는 이유는 실제 함수나 튜플 등의 앱스트랙션과 저들은
전혀 관계가 없기 때문입니다.
JVM에서 클래스파일을 수정안하고, 리플렉션 같은것도 안쓰고 돌릴라니까 꽁수(?)를 쓰는건데, Function1인지
Function2인지는 사실은 구현 세부 사항이라고 볼 수 있거든요.(FunctionX에서 X가 최대 얼마인지 전
까먹었습니다. 기억하시는 분?)

마찬가지로 튜플의 경우에도 실제로는 더 일반적인 튜플로 기억해 두는게 좋겠죠. 물론 처음 자바나 C++쪽에서 온 분이라면
Tuple2[A,B]가 더 알아보기 편하실 수도 있겠지만요.


2014/1/21 최정열 <mye...@gmail.com>:
> --
> Google 그룹스 '라 스칼라 코딩단' 그룹에 가입했으므로 본 메일이 전송되었습니다.
> 이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 scala-korea...@googlegroups.com에 이메일을
> 보내세요.
> 이 그룹에 게시하려면 scala...@googlegroups.com(으)로 이메일을 보내세요.
> 웹에서 이 토론을 보려면
> https://groups.google.com/d/msgid/scala-korea/CAD5fO2rnt8MRM6HHzSTqb%2BZg5yRDFdA2M3P%2BtPCNL8JYTvnLHA%40mail.gmail.com

최정열

unread,
Jan 21, 2014, 12:56:30 PM1/21/14
to scala...@googlegroups.com
저도 스칼라의 스타일을 익히기 위해서는 FunctionX[A, B...] 보다는 A=>B 형식을 이용하는 하는 것이 많이 중요하다고 생각합니다. 

처음에 제가 스칼라를 보면서 자바와의 호환성을 갖추면서 성능의 저하가 별로 없을지 궁금(의심) 했었는데, 그래서 공부하면서 구현방식에 관심을 두었기 때문인 것 같습니다. 결정적으로 자바를 예로 들지 않으면 제능력으로 설명이 어려워진다는;;;


2014년 1월 21일 오후 2:02, Hyunsok Oh <ensh...@gmail.com>님이 작성:
Reply all
Reply to author
Forward
0 new messages