독서관련/Kotlin in Action

CH08 코틀린 고차함수

ColinKang 2021. 1. 28. 22:16
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

 

고차 함수 : high order function : 다른 함수를 인자로 받거나 함수를 반환하는 함수 예 list.filter(x > 0)

고차 함수 정의 : 함수 타입 : 함수 문법 : (Int, String) -> Unit : 파라미터 타입 -> 반환타입

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

val action: () -> Unit = { println(42) }

// 반환 타입 null 될 수 있음
val canReturnNull: (Int, Int) -> Int? = { x, y => null }

// 널이 될 수 있는 함수 타입 변수 정의
var funOrNull: ((Int, Int) -> Int)? = null


 

인자로 받은 함수 호출

fun String.filter(predicate: (Char) -> Boolean): String

fun String.filter(predicate: (Char) -> Boolean) : String {
	val sb = StringBuilder()
    // length = String 내 length
    for (index in 0 until length) {
    	val element = get(index)
        if (predicate(element) sb.append(element)
    }
    return sb.toString()
}

>>> println("ab1c".filter{ it in 'a'..'z'}
abc

fun <T> Collection<T>.joinToString (
	seperator: String = "",
    prefix: String = "",
    postfix: Sring = ""
) : String {
	val result = StringBuilder(prefix)
    for ((index, element) in this.withIndex()) {
    	if (index > 0) result.append(prefix)
        result.append(element)
    }
    result.append(postfix)
    return result.toString()
}

fun <T> Collection<T>.joinToString (
	seperator: String = "",
    prefix: String = "",
    postfix: Sring = "",
    tranform: (T) -> String = { it.toString() }
) : String {
	val result = StringBuilder(prefix)
    for ((index, element) in this.withIndex()) {
    	if (index > 0) result.append(prefix)
        result.append(transform(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(seperator = "! ", postfix = "!", transform = { it.toUpperCase() }))
ALPHA! BETA!


fun <T> Collection<T>.joinToString (
	seperator: String = "",
    prefix: String = "",
    postfix: Sring = "",
    tranform: ((T) -> String)? = null
) : String {
	val result = StringBuilder(prefix)
    for ((index, element) in this.withIndex()) {
    	if (index > 0) result.append(prefix)
        val str = transform?.invoke()
        	?: element.toString()
        result.append(str)
    }
    result.append(postfix)
    return result.toString()
}

 

함수에서 함수 반환

enum class Delivery { STANDARD, EXPRESS }

class Order(val itemCount: Int)

fun getShippingCostCalculator(delivery: Delivery): (Order) -> Double {
	if (delivery == Delivery.EXPRESS) {
    	return { order -> 2.0 * order.itemCount }
    }
    return { order -> 1.5 * order.itemCount }
}

data class Person{
	val firtName: String,
    val lastName: String,
    val phoneNumber: String?
}

class ContactListFilters {
	var prefix: String = ""
    var onlyWithPhoneNumber: Boolean = false
    fun getPredicate(): (Person) -> Boolean {
    	val startWithPrefix = { p: Person -> 
        	p.firstName.startWith(prefix) || p.lastName.startWith(prefix)
        }
        if (!onlyWithPhoneNumber) {
        	return startWithPrefix
        }
        return { startWithPrefix(it) && it.phoneNumber != null}
    }
}

data class SiteVisit(
	val path: String,
    val duration: Double,
    val os: OS
)


enum class OS { WINDOW, 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),
)

fun List<SiteVisit>.averageDurationFor(predicate: (SiteVisit) -> Boolean) = 
	filter(predicate).map(SiteVisit::duration).average()
    
>>> println(log.averageDurationFor { it.os == OS.IOS && it.path = "/signup" })

 

함수 inline, noline

use  함수

withLock함수

non-local변환 : 자신을 둘러싸고 있는 블록보다 더 바깥에 있는 다른 블록을 반환하게 만드는 return문 non-local : 인라인 함수인 경우

return이 바깥쪽 함수를 반환시킨다

인라인 되지 않는 함수에 전달되는 람다안에서 return을 사용할 수 없다.

로컬 return : 람다의 실행을 끝내고 람다를 호출했던 코드이 실행을 계속 이어간다.

return으로 실행을 끝내고 싶은 람다 식 앞에 레이블 lable@ return뒤에 그 레이블 추가

람다식에는 레이블이 2개 이상 붙을 수 없다.

무명함수는 : 코드 블록을 함수에 넘길 때 사용할 수 있는 다른 방법이다.

fun lookForAlice(people: List<Persion>) {
				   // returned
	people.forEach(fun(person) {
    	if (person.name == "Alice") return
    }
}

//returned
fun lookForAlice(people: List<Person>) {
	people.forEach {
    	if (it.name == "Alice") return
    }
}