고차함수(High-Order Function)
매개변수로 함수를 전달받거나 함수를 반환하는 함수를 말합니다.
고차 함수 내에서 매개변수로 함수 타입을 선언하고 고차 함수를 호출하는 곳에서 람다 함수를 전달하는 구조 입니다.
아래는 Int 타입 매개변수 두 개를 전달받아 결과로 Int 타입의 데이터를 반환하는 일반함수와
매개변수로 함수를 받는 예시 입니다.
ex)
fun normalFun(a: Int, b: Int): Int = a + b
//매개변수 b는 Int형 매개변수 하나와 반환값 타입이 Int형인 함수를 의미합니다.
fun highFun(a: Int, b: (Int) -> Int) {
val result = b(10)
println("a = $a, b = $result")
}
// ....
highFun(10, { x -> x * x})
고차 함수와 함수 타입 매개변수
함수 타입의 매개변수 대입
- 고차 함수의
마지막 매개변수가 함수 타입이면 함수를 호출할 때 ()를 생략할 수 있습니다.
ex)
fun hoFun(argFun: (Int) -> Int) {
// ...
}
fun hoFun2(a: Int, argFun1: (Int) -> Int, argFun: (Int) -> Boolean) {
// ...
}
hoFun({ x -> x * x})
hoFun { x ->
x * x
}
hoFun2(10, { it + it}, { it > 10})
hoFun2(10, { it + it}) {
it > 10
}
함수 타입 기본값 이용
- 일반적인 함수의 매개변수에 기본값을 지정하듯
고차함수에서도 기본값을 선언할 수 있습니다.
ex)
fun hoFun(a: Int, b: (Int) -> Int = {x -> x * x }) {
// ...
}
hoFun(a) // 기본값 이용
hoFun(a) {
it * it
}
고차 함수와 함수 반환
- 고차 함수에서 함수를 반환하는 방법은 반환 타입에 함수 타입을 선언 하면 됩니다.
ex)
fun hoFun(str: String) : (a: Int, b: Int) -> Int {
return when (str) {
"-" -> {a,b -> a-b}
"*" -> {a,b -> a*b}
"/" -> {a,b -> a/b}
else -> {a,b -> a+b}
}
}
val resultFun = hoFun("*")
resultFun(10,5)
함수 참조와 익명 함수 이용
- 일반적으로 고차 함수에서 매개변수나 반환값으로 람다 함수를 많이 이용하지만,
함수 참조나 익명 함수를 이용해도 됩니다.
함수 참조를 이용한 함수 전달
- 함수 참조 연산자 콜론 두 개(::) 를 이용하여
다른 함수를 고차 함수의 인자로 이용하는 방법입니다.
fun hoFun(a: (Int) -> Int) {
// ...
}
fun someFun(x: Int): Int {
retunr x * 10
}
// ...
hoFun {
it * 10
}
hoFun(::someFun)
익명 함수를 이용한 함수 전달
- 람다식에는 반환을(return) 명시적으로 해줄 수 없기 때문에 주로 반환을 명시적으로 선언하려고 할 때 이용됩니다.
ex)
// 익명 함수는 단어 그대로 이름이 없는 함수를 뜻하며 기존 함수 선언에서 이름이 빠진 형식이다.
val anonyFun = fun(a: Int): Int = a * a
val anonyFun2 = fun(a: Int): Int {
println("it's anonymous function")
return a + a
}
fun hoFun(x: (Int) -> Int){
val result = x(10)
println("it's High order Function")
}
// ...
hoFun(anonyFun)
hoFun(anonyFun2)
코틀린 API의 유용한 고차함수
run()
inline fun <R> run(block: () -> R): R
inline fun <T, R> T.run(block: T.() -> R): R
-
단순히 람다 함수를 실행하고 그 결괏값을 얻는 목적으로 사용합니다.
- 매개변수에 함수 타입이 있으므로
람다 함수를 전달해 주고 해당 람다 함수의 반환값을 그대로 반환하는 구조
- 매개변수에 함수 타입이 있으므로
-
객체의 멤버에 접근할때 유용하게 사용됩니다.
- run()를 호출한 객체가 람다 함수에 전달되어 객체의 멤버를 바로 이용할 수 있습니다.
ex)
data class User(
var name: String,
var age: Int,
) {
fun sayName() {
println("name = $name")
}
fun checkAdult(): Boolean = age >= 20
}
val result : Int = run {
println("it's run call")
10 + 20 // 마지막 반환
}
val isAdult : Boolean = User("Lee", 27).run {
sayName()
checkAdult()
}
apply()
inline fun <T> T.apply(block: T.() -> Unit): T
- apply()함수는 apply()함수를 호출한
객체를 반환합니다.
ex)
data class User(
var name: String,
var age: Int,
) {
fun sayName() {
println("name = $name")
}
fun checkAdult(): Boolean = age >= 20
}
//...
val user = User("Lee", 27).apply {
name = "kim"
age = 19
sayName()
if (checkAdult())
println("성인입니다.")
else
println("성인이 아닙니다.")
}
let()
inline fun <T, R> T.let(block: (T) -> R): R
-
호출한 객체(
T)를 매개변수인 람다 함수(위의block)에 매개변수(T)로 전달후 해당 람다 함수의 반환값(R)을 그대로 반환(R) 합니다. -
변수 선언을 줄이고자 할 때 사용이 용이합니다.
data class User(
val name: String,
val age: Int
)
// ...
User("Lee",27).let {
println(it.name)
println(it.age)
}
with() 함수
inline fun <T, R> with(receiver: T, block: T.() -> R): R
- run() 함수와 유사 하며 차이점은 run()함수는 자신을 호출한 객체를 이용하지만, with()함수는 매개변수로 지정한 객체를 이용합니다.
data class User(
var name: String,
var age: Int,
) {
fun sayHello() {
println("hello")
}
}
val user = User("lee", 27)
with(user) {
name = "kim"
sayHello()
}
// 동일한 동작
uer.run {
name = "kim"
sayHello()
}
인라인 함수
인라인 함수란?
- 고차 함수에 람다 함수를 전달하고 람다 함수를 이용하는 코드가 많아져서 런타임 때 성능 문제가 발생할 수 있기에, 컴파일 단계에서 inline으로 정의한 함수의 내용이 호출되는 곳에 정적으로포함 되므로 런타임 때 함수 호출이 그만큼 줄고 성능에 도움이 됩니다.
참조 : 깡샘의 코틀린 프로그래밍
