Chapter5. 람다로 프로그래밍

김신영·2022년 10월 14일
0

kotlin-in-action

목록 보기
5/11
post-thumbnail

람다식의 문법

{ x: Int, y: Int -> x + y }
  • Kotlin에서 람다식은 중괄호{, } 로 둘러싸여 있다.
  • 인자 목록 주변에 괄호(, )가 없다.
val sum = { x: Int, y: Int -> x + y }
println(sum(1, 2))  // 3
  • 람다식을 변수에 저장할 수 있다.
  • 람다가 저장된 변수를 일반 함수와 마찬가지로 호출할 수 있다.
{ println(42) }()    // 42

run { println(42) }  // 42
data class Person(val name: String, val age: Int)

val people = listOf(Person("Alice", 29), Person("Bob", 31))

people.maxBy({ p: Person -> p.age })
people.maxBy() { p: Person -> p.age }
poeple.maxBy { p: Person -> p.age }
people.maxBy { p -> p.age }
people.maxBy { it.age }
  • Kotlin에서는 함수 호출 시 맨 뒤에 있는 인자가 람다식이라면, 그 람다를 괄호 밖으로 뺄수 있다.
  • 람다가 함수의 유일한 인자이면, 괄호를 생략해도 된다.
  • 인자 목록의 맨 마지막 람다만 밖으로 뺄수 있다.
  • 파라미터 중 일부의 타읍은 지정하고, 나머지 파라미터는 타입을 지정하지 않고 이름만 남겨줘도 된다.
  • 람다의 파라미터가 하나뿐이고, 그 타입을 컴파일러가 추론할 수 있는 경우, it 디폴트 파라미터를 사용할 수 있다.

NOTE
중첩된 람다에서는 가급적 타입을 명시해주는 것이 좋다.
it를 중첩적으로 사용하는것이 좋지는 않다.

  • 본문이 여러줄인 람다식의 경우, 맨 마지막에 있는 식이 람다의 리턴 값이 된다.
val sum = { x: Int, y: Int ->
	println("Computing the sum of ${x} and ${y}...")
	x + y
}

멤버 참조 ::

people.maxBy(People::age)

최상위 함수/프로퍼티 참조

fun salute() = println("Salute!")

run(::salute)

생성자 참조

data class Person(val name: String, val age: Int)  
  
fun main(args: Array<String>) {  
    val createPerson = ::Person  
    val p = createPerson("Alice", 29)  
    println(p)  // Person(name=Alice, age=29)
}

바운드 멤버 참조

val ageFunction = Person::age  // 수신 객체 파라미터를 받아서 age를 리턴하는 람다식

val printAgeFunction = { person: Person -> println("${person.name} is ${ageFunction(person)} years old") }  

printAgeFunction(p)  // Alice is 29 years old

val boundedMemberRef = p::age
println(boundedMemberRef())  // 29

Collection 함수형 API

filterKeys, filterValues, mapKeys, mapValues

val numbers = mapOf(0 to "zero", 1 to "one")  
println(numbers.mapValues { it.value.toUpperCase() })  
println(numbers.mapKeys { "${it.key}_${it.value}" })  
println(numbers.filterKeys { it % 2 == 0 })  
println(numbers.filterValues { it.startsWith("o") })

any, all, count, find


val people = listOf(Person("Alice", 27), Person("Bob", 31))  
println(people.find(canBeInClub27))  
println(people.firstOrNull(canBeInClub27))
  • find == firstOrNull

groupBy

val list = listOf("a", "ab", "b", "c", "abc", "bc", "bcd", "cd", "d")  
println(list.groupBy(String::first))
// {a=[a, ab, abc], b=[b, bc, bcd], c=[c, cd], d=[d]}

flatMap, flatten

  • flatMap == map with toList and flatten
val books = listOf(Book("Thursday Next", listOf("Jasper Fforde")),  
                   Book("Mort", listOf("Terry Pratchett")),  
                   Book("Good Omens", listOf("Terry Pratchett",  
                                             "Neil Gaiman")))  
println(books.flatMap { it.authors }.toSet())  
// [Jasper Fforde, Terry Pratchett, Neil Gaiman]
val listOfLists = listOf(listOf(1,2,3), listOf(4,5,6), listOf(2,5,7))  
println(listOfLists.flatten())
// [1, 2, 3, 4, 5, 6, 2, 5, 7]

Lazy Collection 연산

Sequence (asSequence())

println(  
    listOf(1, 2, 3, 4).asSequence()  
        .filter { it * it < 5 }  
        .map { it * it }  
        .toList()  
)

Sequence 만들기 generateSequence

val naturalNumbers = generateSequence(0) { it + 1 }  
val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }  
println(numbersTo100.sum())
fun File.isInsideHiddenDirectory() =  
        generateSequence(this) { it.parentFile }.any { it.isHidden }  
        
fun File.getInsideHiddenDirectory() =  
        generateSequence(this) { it.parentFile }.firstOrNull{ it.isHidden}
  
fun main(args: Array<String>) {  
    val file = File("/Users/svtk/.HiddenDir/a.txt")  
    println(file.isInsideHiddenDirectory())  // true
    println(file.getInsideHiddenDirectory()) // /Users/svtk/.HiddenDir

	val file2 = File("/Users/svtk/Documents/a.txt")
	println(file2.isInsideHiddenDirectory())   // false
	println(file2.getInsideHiddenDirectory())  // null
}

SAM 생성자

SAM
Single Abstract Method (= Funtional Interface)

  • 함수형 인터페이스의 인스턴스를 반환하는 메소드가 있다면, 람다를 직접 반환할 수 없다.
  • 반환하고픈 람다를 SAM 생성자로 감싸야 한다.
fun createAllDoneRunnable(): Runnable {  
    return Runnable { println("All done!") }  
}  
  
fun main(args: Array<String>) {  
    createAllDoneRunnable().run()  // All done!

	// { println("All done?!")}.run() // Compile Error
}

수신 객체 지정 람다 with, apply

with

  • with 함수는 첫 번째 인자로 받은 객체를 두 번째 인자로 받은 람다의 수신 객체로 만든다.
  • 마지막 식을 리턴한다.

Before

fun alphabet(): String {  
    val result = StringBuilder()  
    for (letter in 'A'..'Z') {  
         result.append(letter)  
    }  
    result.append("\nNow I know the alphabet!")  
    return result.toString()  
}

After

fun alphabet() = with(StringBuilder()) {  
    for (letter in 'A'..'Z') {  
        this.append(letter)  // this 생략 가능
    }  
    append("\nNow I know the alphabet!")  
    toString()  
}

apply

  • apply 함수는 거의 with 함수와 비슷하다.
  • 차이점은 apply는 항상 자신에게 전달된 수신 객체를 반환한다.
fun alphabet() = StringBuilder().apply {  
    for (letter in 'A'..'Z') {  
        append(letter)  
    }  
    append("\nNow I know the alphabet!")  
}.toString()

buildString

fun alphabet() = buildString {  
    for (letter in 'A'..'Z') {  
        append(letter)  
    }  
    append("\nNow I know the alphabet!")  
}
profile
Hello velog!

0개의 댓글