728x90
반응형
고차함수?
다른 함수를 인자로 받거나 함수를 반환하는 함수
(파라미터 혹은 반환 값으로 람다 사용)
함수 타입이란?
- 함수 타입은 아래와 같이 선언
// 타입 추론
val sum = { x: Int, y: Int -> x + y }
// 구체적인 타입 선언
val sum: (Int, Int) -> Int = { x, y -> x + y }
인자로 받은 함수 호출
- 간단한 고차 함수 정의
fun twoAndThree(operation: (Int, Int) -> Int) {
val result operation(2, 3)
println("The result is $result")
}
함수 타입 파라미터에 디폴트 값 지정
fun <T> Collection<T>.joinString(
separator: String = ", ",
prefix: String = "",
postfix: String = "",
transform: (T) -> String = { it.toString() } // default 지정
) {
.. blah blah
transform(element)
}
println(letters.joinToString())
println(letters.joinToString(separator = "! ", transform = { it.toUpperCase() }))
널이 될 수 있는 함수 타입 파라미터
fun foo(callback: (() -> Unit)?) {
// invoke로 안전 호출 가능 (함수 타입 -> invoke 메소드를 구현하는 인터페이스)
callback?.invoke()
}
fun <T> Collection<T>.joinString(
separator: String = ", ",
prefix: String = "",
postfix: String = "",
transform: ((T) -> String)? = null
) {
.. blah blah
val str = transform?.invoke(element) ?: elment.toString()
}
함수 반환
- 함수의 반환 타입으로 함수 타입을 지정해야 한다.
enum class Delivery { STANDARD, EXPEDITED }
class Order(val itemCount: Int)
fun getShippingCostCalculator(delivery: Delivery): (Order) -> Double {
if (delivery == Delivery.EXPEDITED) {
return { order -> 6 + 2.1 * order.itemCount }
}
return { order -> 1.2 * order.itemCount }
}
val calculator = getShippingCostCalculator(Delivery.EXPEDITED)
println("Shipping costs ${calculator(Order(3))}")
람다를 활용한 중복 제거
- 람다를 사옹할 수 없는 환경에서는 아주 복잡한 구조를 만들어야만 피할 수 있는 코드 중복도 람다를 활용하면 간결하고 쉽게 제거할 수 있다.
// 웹 사이트 방문 기록을 분석
data class SiteVisit(
val path: String,
val duration: Double,
val os: OS
)
enum class OS { WINDOWS, LINUX, MAC, IOS, ANDROID }
val log = listOf(
SiteVisit("/", 34.0, OS.WINDOWS),
SiteVisit("/", 22.0, OS.MAC),
SiteVisit("/login", 12.0, OS.WINDOWS),
SiteVisit("/signup", 8.0, OS.IOS),
SiteVisit("/", 16.3, OS.ANDROID)
)
- 하드코딩한 필터를 사용해 방문 데이터 분석
val averageMobileDuration = log
.filter( it.os in setOf(OS.IOS, OS.ANDROID) }
.map(SiteVisit::duration)
.average()
println(averageMobileDuration)
// 12.15
- 고차 함수를 사용해 중복 제거하기
fun List<SiteVisit>.averageDurationFor(predicate: (SiteVisit) -> Boolean) =
filter(predicate).map(SiteVisit::duration).average()
println(log.averageDurationFor {
it.os in setOf(OS.IOS, OS.ANDROID)
})
// 12.15
println(log.averageDurationFor {
it.os == OS.IOS && it.path == "/signup"
})
- 함수 타입을 언어가 지원하면 전략 패턴을 간단하게 구현할 수 있다.
- 인터페이스 선언, 구현 클래스를 통해 전략 정의할 필요가 없음
- 일반 함수 타입을 사용해 여러 전략을 전달할 수 있음
- 고차 함수를 활용하면 전통적인 루프와 조건문을 사용할 때보다 더 느려지지 않을까??
- 항상 더 느린 것은 아님. 어떻게 람다의 성능을 개선할 수 있을지 알아보자 -> 인라인 함수 참고
고차함수 안에서 흐름 제어
람다를 둘러싼 함수로부터 반환 : 람다 안의 return
- 람다 안에서 return을 사용하면 람다로부터 반환되는 것이 아니라 람다를 호출하는 함수가 실행을 끝내고 반환된다.
- 자신을 둘러싸고 있는 블록보다 바깥에 있는 다른 블록을 반환하게 만드는 return 문을
넌로컬(non-local) return
이라 부른다.
data class Person(val name: String, val age: Int)
val peopole = listOf(Person("Alice", 29), Person("Bob", 31))
fun lookForAlice(people: List<Person>) {
people.forEach {
if (it.name == "Alice") {
println("Found") // lookForAlice 함수에서 반환됨 (바깥에 있는 다른 블록 반환)
return
}
}
println("Alice is not found") // 해당 로직은 타지 않게 됨
}
- return이 바깥쪽 함수를 반환시킬 수 있는 때는 람다를 인자로 받는 함수가
인라인 함수
인 경우이다. - 인라이닝되지 않는 함수에서는 전달되는 람다안에서 return을 사용할 수 없다.
람다로부터 반환 : 레이블을 사용한 return
- 람다 식에서도
로컬(local) return
을 사용할 수 있다. - return으로 실행을 끝내고 싶은 람다 식 앞에 레이블을 붙이고, return 키워드 뒤에 그 레이블을 추가한다.
fun lookForAlice(people: List<Person>) {
people.forEach label@{ // 람다식 앞에 레이블을 붙인다.
if (it.name == "Alice") return @label
}
println("Alice might be somewhere") // 항상 이 줄이 출력된다.
}
StringBuilder().apply sb@{ // this@sb를 통해 람다의 묵시적 수신 객체에 접근 가능
listOf(1, 2, 3).apply {
this@sb.append(this.toString()) // this는 가장 안쪽 수신 객체
}
}
무명함수 : 기본적으로 로컬 return
- fun 키워드를 사용해 정의된 가장 안쪽 함수를 반환
- 람다식은 fun이 사용되지 않으므로, 본문의 return은 람다 밖의 함수를 반환
- 무명함수는 fun을 사용하므로 자신이 바로 가장 안쪽에 있는 fun으로 정의된 함수가 된다.
fun lookForAlice(people: List<Person>) {
peopole.forEach(fun (person) { // 람다식 대신 무명 함수
if (it.name == "Alice") return
println("${person.name} is not Alice")
}
}
- 무명 함수는 일반함수와 다르게 함수 이름과 파라미터 타입을 생략할 수 있다.
728x90
반응형
'프로그래밍 노트 > Kotlin' 카테고리의 다른 글
[Kotlin] 코루틴 찍먹 (1) | 2024.03.18 |
---|---|
[Kotlin] 구조 분해 선언 활용하기 (0) | 2023.07.07 |
[Kotlin] 컬렉션과 범위에서 사용할 수 있는 관례 정리 (0) | 2023.06.29 |
[Kotlin] 산술/비교 연산자 오버로딩 (0) | 2023.06.29 |
[Kotlin] 코틀린 데이터 타입 (0) | 2023.03.27 |