Kotlin 고차함수와 람다를 예제를 통해 익히자!

KIM 쥬얼리 (vs0610)·2021년 5월 28일
3
post-thumbnail

📌 글을 시작하기 전에

이 글은 나의 무지를 반성하고 더 깊은 지식의 갈구를 하기 위해서 작성되었다. 코틀린을 사용해 picpho라는 앱 서비스를 5주의 개발과정을 거쳐서 출시하였다. 아무리 절대적인 시간이 적었다 하더라도 개발 지식을 보충할 시간은 충분했다고 생각한다. 더 깊게 알아보지 않았을 뿐.

5주의 개발과정이 끝나고 서초 펍지 사옥에서 현업에 계신 개발자들앞에서 서비스를 발표를 한 뒤 질문을 받는 시간이 있었다. 어떤 개발자분께서 Kotlin이 함수형 프로그래밍을 지원하는 언어인데 이를 알고 사용하신거냐는 질문을 하셨다. 내가 프로젝트에 코틀린을 사용했던 이유는 다음 두 가지였다.

1 . JAVA보다 사용자 친화적 언어로 배움이 빠르기 때문에 5주라는 짧은 시간내에 익히고 개발하기에 적합하다고 생각했다.
2 . 구글에서 지원하는 언어이고 차세대로 갈수록 각광받을 언어라는 단순 미래지향적인 생각을 하였다.

따라서 함수형 프로그래밍에 대한 깊은 고민을 하지 않고 선택했었던 것이다. 그러니 관련내용에 대해서 잘 알지 못하는 것은 또한 당연했다. 또, 어떤 기업의 코딩테스트를 합격하고 1차 기술 면접을 볼 때 이런 질문을 받은 적이 있다. Kotlin에서 고차함수에 대해서 알고 있느냐 라는 질문이었다. 당시 고차함수에 대한 개념정도는 알고있었는데 정말 딱 그정도였다. 그래도 나름 안드로이드 쪽에서 나오는 질문들을 대부분 대답할 수 있겠다고 생각하고 있었는데 정말 간단한 내용에서 대답을 잘 못하는 내 자신을 반성하게 되었다.

그래서 이 글을 작성한다. 단순히 개념정도를 아는 것에서 벗어나 실제 코드를 작성해보고 이해해보려고 한다.

💁🏻‍♂️ 먼저 함수형 프로그래밍이란?

먼저 위키백과에서 정의를 본다.

함수형 프로그래밍(functional programming)은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다.

코드를 작성하면서 가변 변수들을 사용하면서 생길 수 있는 문제들을 배제하려는 프로그래밍 방식이다. x가 나왔을 때 무조건 f(x)라는 값이 정해져있는 수학적 함수 방식을 가진 프로그래밍이다.

💁🏻‍♂️ 고차함수란?

고차함수는 함수를 인수로 취하거나 혹은 결과로 반환하는 함수를 고차함수라고 한다. 안드로이드를 개발하다보면 굉장히 많이쓰는 setOnclickListner같은 콜백 함수가 고차함수에 속한다.

🙋‍♂️ 람다란?

람다 대수는 함수를 단순하게 표현할 수 있도록 도와주는 개념이다. 중괄호로 묶어 사용할 수 있다.

< 사용 예시 >
{ 변수1 : 타입, 변수2 : 타입 -> 변수1 + 변수 2 }

위와 같은 방식으로 사용할 수 있다. 고차함수의 인자로 사용할 수 있어서 고차함수와 꼭 같이 나오는 개념이다. 또한 함수가 이름을 가질 필요가 없는 익명함수이기도 하다.

👍 직접 코딩하며 예제를 공부하자!

HighorderFuctionAndLambda.kt 라는 코틀린 파일을 만들어서 예제를 공부한다.

main 함수를 선언해준다.

fun main{
	// TODO
}

Lambda를 먼저 사용해보자.

original format

람다 함수는 변수로 선언할 수도 있다.

val sum : (Int, Int) -> Int = {x, y -> x+y}
	(타입, 타입) -> 반환형 타입 

simple format

간단하게 표현하면 다음과 같다.

val sum = {x : Int, y : Int -> x + y}

main에서 호출을 해보자.

// main
In : println(sum(2,3))
Out : 5

람다를 활용해서 고차함수 만들어보기

fun Calculator(a : Int, b : Int, p: (Int, Int) -> Int){ 
// Calculator는 고차함수라고 말할 수 있다.
    println("$a, $b -> ${p(a, b)}")
}

a, b와 두 개의 Int 인자를 받아 하나의 Int 반환값을 내는 함수를 인자로 받아 이를 출력하는 Calculator라는 고차함수를 만들었다. main에서 호출을 해보자.

In : Calculator(2, 4, { a : Int, b : Int -> a + b})
Out : 2, 4 -> 6

이때 lambda 함수가 인자의 제일 마지막에 있다면 밖으로 뺄 수 있다.

In : Calculator(2, 4) { a: Int, b: Int -> a + b }
Out : 2, 4 -> 6

또한 고차함수에 타입이 정의되어있는 경우 타입을 생략할 수 있다.

In : Calculator(3, 5) { a, b -> a + b }
Out : 3, 5 -> 8

람다함수가 아닌 일반함수를 인자로 넣으려면 일반함수 앞에 ::를 붙여 다음과 같이 하면 된다.

fun sum(a : Int, b : Int) = a + b
// main
In : Calculator(5,7, ::sum)
Out : 5, 7 -> 12

함수형 변수를 인자타입에 넣는 경우는 다음과 같이 한다.

In[0] : val minus : (Int, Int) -> Int = {a, b -> a-b}
In[1] : Calculator(5,2, minus)
Out : 5, 2 -> 3

고차함수의 인자가 하나인 경우

fun Square(a : Int, p: (Int) -> Int){
    println("square $a -> ${p(a)}")
}

위와 같이 a라는 인자 하나를 받는 경우 조금 다르게 표현할 수도 있는데 다음과 같다.

In : Square(3) {a -> a * a}
Out : square 3 -> 9
In : Square(3) {it * it}
Out : square 3 -> 9

위 두 호출은 같은 호출이다. 다만 인자가 하나일 때는 it 을 사용해 더 간단하게 표현이 가능하다.

의미있는 반환값이 없는 경우

fun PrintInfo(p : () -> Unit) {
	print("Calculator Version : ")
    p()
}

위와 같이 의미있는 반환값이 없을 경우 Unit을 사용한다. main에서 호출해보자.

// main 
In : PrintInfo() {println("1.0")}
Out : Calculator Version : 1.0

방금 경우와 같이 고차함수의 인자로 매개변수없이 함수식만 있는 경우 소괄호() 는 생략가능하다.

// main
In : PrintInfo {println("1.1")}
Out : Calculator Version : 1.1

또한 매개변수 함수식을 Nullable로 할수도 있다.

fun PrintInfo(p : (() -> Unit)? = null{
	print("Calculator Version : ")
    p?.invoke()?: println("no version")
}

함수식에 null이 들어왔을 경우 ?: 기호 뒤의 식을 실행한다. main에서 호출해보자.

In : PrintInfo()
Out : Calculator Version : no version

📌 글을 마치며

글을 쓰며 공부하다보니 프로젝트를 진행하면서 고차함수와 람다를 알게모르게 많이 썼다는 사실을 알게되었다. 여행지를 가기 전에 미리 여행지에 대한 정보를 공부하고 가면 가서 보이지 않던 것이 보이고 더 새로운 느낌으로 다가오는 경험을 해본 적이 있다. 이처럼 그냥 사용할 수 있는 프로그래밍을 하더라도 자세히 공부해보고 사용해본다면 분명 더 발전하는 경험을 할 수 있을 것이라는 생각이 든다. 👍

🥰 도움받은 자료

몽구스 프로그래밍 / 네이버 블로그
함수형 프로그래밍 / wiki
고차함수 / wiki
람다대수 / wiki

0개의 댓글