-
[코틀린을 다루는 기술] 코틀린 함수 정리Kotlin 2020. 7. 31. 15:13
이 글은 길벗 사의 '코틀린을 다루는 기술' 을 읽으면서 정리한 포스팅입니다.
더 자세한 내용은 '코틀린을 다루는 기술' 책에서 찾아보시면 좋을 것 같습니다.서론
디프만 8기에서 코틀린 스터디를 진행중입니다. 이번 스터디에서는 '코틀린을 다루는 기술'이라는 책을 토대로 코틀린 사용법과 활용도를 높이고자 하는 목표를 가지고 있습니다. 특히 단순히 항상 쓰던 문법만이 아닌, 더욱 '코틀린스럽게', '코틀린답게' 코딩을 하도록 실력을 키우는 것이 중요하다고 생각합니다.
순수 함수
이 책의 초반에서 가장 강조하는 부분은 안전한 프로그래밍을 하는 것입니다. 안전한 프로그래밍을 하기 위한 여러 방법들이 있는데, 3장 함수에서는 계산 시 순수 함수만 사용하는 것과 계산 결과를 외부 세계에서 사용하려면 순수 효과만 사용한다라고 말합니다. 순수 함수란 내부에 아무 효과가 없어서 오로지 인자에 의해서만 반환 값이 결정되는 함수입니다. 즉 같은 인자에 대해선 같은 결과만을 내는 것입니다. 이 말은 인자만 같은 값을 넣으면 다른 결과로 나올 가능성이 아얘 없다는 것이므로, 예기치 못한 에러를 방지할 수 있다는 것입니다.
fun append(i: Int, list: MutableList<Int>): List<Int> { list.add(i) return list } fun append2(i: Int, list: List<Int>) = list + i
append는 list를 반환하는 함수입니다. append는 순수 함수일까요? 순수 함수는 내부에 아무 효과가 없어야 합니다. 하지만 list.add 코드를 실행하는 순간 외부의 list라는 변수를 변화시킵니다. list는 i를 더한 리스트가 되는 셈이죠. 따라서 append 함수는 순수 함수가 아닙니다.
반면, append2는 list의 마지막 인덱스에 i를 더한 새로운 리스트를 반환하는 함수입니다. 따라서 append2는 순수 함수입니다.
커리한 함수
함수 파트에서는 전반적으로 함수 커링에 대해서 강조를 많이 합니다. 저는 이 책에서 처음 들어본 용어인데요, 인자를 여러개 받는 함수를 분리해 인자를 하나씩 받는 함수의 체인으로 만드는 기법을 말합니다.
fun greet(greeting: String, name: String) { println(greeting + " , " + name) }
라는 함수가 있다면, 이 함수는 인자를 두개(정확히는 두 변수쌍을 가진 튜플)를 가지고 있습니다. 이 함수를 커리하려면 어떻게 해야할까요?
fun greeting(greeting: String) { return function(name: String) { println(greeting + " , " + name) } }
함수 재사용하기
함수를 재사용하려면 타입 파라미터를 이용해야합니다. 즉, 제네릭 타입을 활용해야한다는 말이죠.
예를 들어서 Int에서 Int로 가는 두 함수의 합성을 반환하는 compose 함수를 정의해보겠습니다.
fun compose(f: (Int) -> Int, g: (Int) -> Int): (Int) -> Int = {x -> f(g(x))}
라는 함수가 있을 때 이 함수는 Int에서 Int로 가는 함수만을 합성할 수 있습니다. 다른 타입의 함수도 합성하려면 어떻게 정의해야할까요? 위에서 말했듯 타입 파라미터를 사용하면됩니다.
fun <T,U,V> compose(f: (U) -> V, g: (T) -> U): (T) -> V = {f(g(it))}
주의할 점은 f와 g의 타입을 바꿔서는 안된다는 것입니다. 우측의 람다에서는 g를 호출하면서 인자를 넣어주고 있습니다. 그러한 결과가 f의 인자로 넘어가고 다시 f의 결과값으로 반환됩니다. 그래서 함수 리턴타입은 (T) -> V가 됩니다.
람다 함수
코틀린에서 사용하는 함수를 전반적으로 이해하려면 람다식에 대한 이해가 필수적입니다. 저 역시 람다식에 대한 내용은 많이 접해왔으나 정작 제대로 이해하지 못하고 있었고, 실제로 활용하는데서 어려움을 겪었습니다. 람다식에 대해 한번 더 짚고 넘어갈 필요가 있을 것 같습니다.
람다식은 익명 함수를 지칭하는 용어입니다. 코드를 간결하게 하는데 매우 중요한 역할을 하지만, 반대로 남용할 시에는 코드를 이해하기 어려워질 수도 있다는 단점도 있죠.
{x : Int, y: Int -> x + y}
- 람다는 중괄호 사이에 위치합니다. 하지만 경우에 따라서 중괄호를 생략할 수도 있습니다.
- '->'가 인자와 본문을 구별합니다.
- 람다 식은 변수에 저장할 수 있습니다. (함수 리터럴)
- 람다 식을 직접 호출할 수도 있습니다.
val sum = {x: Int, y: Int -> x + y} print(sum(1,2))
이런 식으로 람다를 사용할 수도 있습니다. 이 경우는 변수에 람다를 저장하고 해당 변수를 사용하는 형태가 되겠네요.
확장 함수
코틀린은 기존 클래스에 메소드를 추가할 수 있습니다. 이를 확장 함수라고 하는데요.
예를 들면,
fun String.compose() : String = ... val string1 : String = "mString" string1.compose()
와 같이 기존 자바 String 클래스에는 compose라는 함수가 없지만 마치 함수가 있는 것처럼 사용할 수 있는 것이죠.
이 장에서도 확장 함수에 대한 설명이 자주 나옵니다. 확장 함수는 안드로이드 프레임워크에서도 유용하게 사용되는 기능 중 하나죠.
참조 : javacafe Techblog
'Kotlin' 카테고리의 다른 글
Kotlin의 고차 함수 (0) 2021.05.23 Kotlin의 Null 처리 (0) 2021.05.17 코틀린 수신 객체 지정 람다 : with와 apply (0) 2020.08.07 [코틀린을 다루는 기술] Kotlin에서의 재귀 함수 사용 (0) 2020.08.07 [Kotlin] 배열 사용법 정리 (2) 2020.02.20