Rooted In Develop

Kotlin - 고차 함수 본문

Back End/Kotlin

Kotlin - 고차 함수

RootedIn 2023. 6. 3. 20:20

고차 함수 : 다른 함수를 인자로 받거나 함수를 반환하는 함수

함수 타입

val sum: (Int, Int) -> Int = ( x, y -> x + y }
val action: () -> Unit = { println(1) }

문법 : 파라미터 타입 -> 반환 타입

인자로 받은 함수 호출

fun twoAndThree(operation: (Int, Int) -> Int) {
  val result = operation(2, 3)
  println("result = $result")
}
>>> twoAndThree { a, b -> a + b }
result = 5
>>> twoAndThree { a, b -> a * b }
result = 6

자바에서 코틀린 함수 타입 사용

// Kotlin
fun processTheAnswer(f: (Int) -> Int) {
  println(f(42))
}

// Java
>>> processTheAnswer(number -> number + 1);
43

디폴트 값을 지정한 함수 타입 파라미터나 널이 될 수 있는 함수 타입 파라미터

fun <T> Collection<T>.joinToString(
  separator: String = ", ",
  prefix: String = "",
  postfix: String = "",
  transform: (T) -> String = { it.toString() }
): String {
  val result = StringBuilder(prefix)
  for ((index, element) in this.withIndex()) {
    if (index > 0) result.append(separator)
    result.append(element)
  }
  result.append(postfix)
  return result.toString()
}
>>> val letters = listOf("Alpha", "Beta")
>>> println(letters.joinToString())
Alpha, Beta
>>> println(letters.joinToString { it.toLowerCase() }
alpha, beta
>>> println(letters.joinToString(separator = "! ", postfix = "! ", transform = { it.toUpperCase() }))
ALPHA! BETA!

함수에서 함수를 반환

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(calculator(Order(3)))
12.3

람다를 활용한 중복 제거

data class SiteVisit(val path: String, val duration: Double, val os: OS)
eunm 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 averageWindowsDuration = log.filter { it.os == OS.WINDOWS }
  .map(SiteVisit::duration)
  .average()
>>> println(averageWindowsDuration)
23.0
// 일반 함수
fun List<SiteVisit>.averageDurationFor(os: OS) = filter { it.os == os }.map(SiteVisit:duration).average()
// 고차 함수
fun List<SiteVisit>.averageDurationFor(predicate: (SiteVisit) -> Boolean) = filter(predicate).map(SiteVisit::duration).average()
>>> println(log.averageDurationFor { it.os in setOf(OS.ANDROID, OS.IOS) })
12.15
>>> println(log.averageDurationFor { it.os == OS.IOS && it.path == "/signup" })
8.0

인라인 함수

- 함수를 호출하는 바이트코드 대신에 함수 본문을 번역한 바이트코드로 컴파일함

inline fun <T> synchronized(lock: Lock, action: () -> T) : T {
  lock.lock()
  try {
    return action()
  }
  finally {
    lock.unlock()
  }
}

fun foo(l: Lock) {
  println("Before Sync")
  synchronized(l) {
    println("Action")
  }
  println("After Sync")
}
// 컴파일
fun __foo__(l: Lock) {
  println("Before Sync")
  l.lock()
  try {
    println("Action")
  } finally {
    l.unlock()
  }
  println("After Sync")
}

filter, map : 인라인 함수

람다를 인자로 받는 함수를 인라이닝할 때 이익이 많다.

fun readFirstLineFromFile(path: String): String {
  BufferedReader(FileReader(path)).use { br ->
    return br.readLine() // non-local return
  }
}

람다 안의 return

data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
fun lookForAlice(people: List<Person>) {
  for (person in people) {
    if (person.name == "Alice") {
      println("Found")
      return
    }
  }
  println("Not Found")
}

fun lookForAlice(people: List<Person>) {
  people.forEach {
    if (it.name == "Alice") {
      println("Found")
      return // non-local return
    }
  }
  println("Not Found")
}

non-local return문 : 더 바깥에 있는 다른 블록을 반환하게 하는 return문

람다로부터 반환: 레이블을 사용한 return

fun lookForAlice(people: List<Person>) {
  people.forEach label@{ // 레이블 추가
    if (it.name == "Alice") return@label // 앞에서 정의한 레이블 참조
  }
  println("Alice might be somewhere") // 항상 출력
}

fun lookForAlice(people: List<Person>) {
  people.forEach {
    if (it.name == "Alice") return@forEach
  }
  println("Alice might be somewhere") // 항상 출력
}

무명 함수: 기본적으로 로컬 return

fun lookForAlice(people: List<Person>) {
  people.forEach(fun (person) {
    if (it.name == "Alice") return
    println("${person.name} is not Alice")
  })
}

people.filter(fun (person): Boolean {
  return person.age < 30
})

people.filter(fun (person) = person.age < 30)

- 무명 함수 본문의 return은 무명 함수를 반환시키고, 함수 밖의 다른 함수를 반환시키지 못한다.

'Back End > Kotlin' 카테고리의 다른 글

Kotlin - annotation, reflection  (2) 2023.06.17
Kotlin - 제네릭스  (0) 2023.06.10
Kotlin - 연산자 오버로딩  (0) 2023.05.27
Kotlin - 타입 시스템  (0) 2023.05.06
Kotlin - 클래스, 객체, 인터페이스  (0) 2023.05.01
Comments