ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kotlin]무작정 따라하기3
    Android(+ Kotlin) 2019. 11. 11. 19:36

    무작정 따라 하기 2

    https://charko.tistory.com/8

     

    Kotlin 무작정 따라하기2

    무작정 따라 하기 1 https://charko.tistory.com/7 Kotlin 무작정 따라하기 1 https://codelabs.developers.google.com/codelabs/kotlin-bootcamp-basics/#1 Kotlin Bootcamp for Programmers 2: Kotlin basics In..

    charko.tistory.com

    https://codelabs.developers.google.com/codelabs/kotlin-bootcamp-functions/#1

     

    Kotlin Bootcamp for Programmers 3: Functions

    There's a lot in this lesson, especially if you're new to lambdas. A later lesson revisits lambdas and higher-order functions. Note: You may have noticed that in Kotlin, as in some other languages, there is more than one correct way to do things. Making co

    codelabs.developers.google.com

    부트캠프 기준으로 진행합니다.

     

    이번 챕터에서는 상위함수&람다, 코틀린 코딩문법, when, filter에 대해서 알아보자.

     

    * value에 거의 모든(?)내용을 저장할 수 있다

    fun main(args: Array<String>) {
        val isUnit = println("This is an expression")
        println(isUnit)
    }
    
    // => This is an expression
    // kotlin.Unit

    println은 출력하는 함수이다. 이 함수를 실행과 동시에 isUnit이라는 상수에 저장을 했고, 다시 상수를 출력한다.

    여기서 출력된 kotlin.Unit 을 주목하자! (java의 void와 같음)

    이 타입은, 코틀린의 거의 모든 표현을 value에 저장할 수 있다.

     

    val temperature = 10
    val isHot = if (temperature > 50) true else false
    println(isHot)
    
    // => false
    
    val message = "The water temperature is ${if (temperature > 50) "too warm" else "OK"}"
    println(message)
    
    // => The water temperature is OK

    두 가지의 예가 있다.

    첫 번째

    if의 표현식의 결과값을 isHot의 상수에 저장되어 출력이 가능하다.

    - println 전 temperature의 변수를 바꿔 봤지만 isHot에 저장될 때 이미 if문이 동작한 결과값을 갖고 있다. 

    두 번째는 텍스트에 대한 내용을 저장했다. 첫번째와 두번째는 비슷한 기능이다.

     

    * 루프문은 value에 저장할 수 없다. 루프를 정확한 값이 없기 때문에 컴파일러에서 오류가 발생한다.

     

    * 함수

    feedThefish()
    // => Today is Tuesday and the fish eat pellets
    
    
    fun feedThefish() {
        val day = randomDay()
        val food = "pellets"
        println("Today is $day and the fish eat $food")
    }
    
    fun randomDay() : String {
        val week = arrayOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
        return week[Random().nextInt(week.size)]
    }
    

    기본적인 함수를 만드는 방법이다. 

    코틀린의 함수 선언 방법은 "fun"을 사용한다. fun 함수이름() : 리턴데이터타입

    리턴데이터 타입이 없을 때는 : 이하 구문 생략

     

    when문 사용하여 다시 만들어 보면

    val day = randomDay()
    val food = fishFood(day)
    println("Today is $day and the fish eat $food")
    
    // => Today is Friday and the fish eat mosquitoes
    
    fun fishFood(day: String) : String {
        var food = ""
        when (day) {
            "Monday" -> food = "flakes"
            "Tuesday" -> food = "pellets"
            "Wednesday" -> food = "redworms"
            "Thursday" -> food = "granules"
            "Friday" -> food = "mosquitoes"
            "Saturday" -> food = "lettuce"
            "Sunday" -> food = "plankton"
        }
        return food
    }
    
    fun fishFood(day: String) : String {
        return when (day) {
            "Monday" -> "flakes"
            "Tuesday" -> "pellets"
            "Wednesday" -> "redworms"
            "Thursday" -> "granules"
            "Friday" -> "mosquitoes"
            "Saturday" -> "lettuce"
            "Sunday" -> "plankton"
            else -> "nothing"
        }
    }

    when으로 텍스트 매칭 시 해당 food에 맞는 값을 리턴 시킨다.(var 사용하여 계속된 변경이 가능하다)

    var 제거 후 when문 앞 return을 진행할 수도 있다.(더욱 깔끔해 보인다.)

    * 후자는 리턴값이 없을 수 있음으로 else로 예외상황을 제거하여야 한다.

     

    * 편리한 함수

    swim()
    swim("slow")
    swim(speed = "turtle-like")
    
    fun swim(speed: String = "fast") {
        println("swimming $speed")
    }
    
    // => swimming fast
    // swimming slow
    // swimming turtle-like
    
    
    swim("a", "b")
    swim(place = "see", speed = "turtle-like")
    
    fun swim(speed: String = "fast", place: String = "pool") {
        println("swimming $speed in the $place")
    }
    
    // => swimming a in the b
    // swimming turtle-like in the see

     

     

    1. 함수의 초기값을 줄 수 있어 별도의 파라미터 없이 사용 가능.

    (초기값이 없다면 파라미터는 항상 지정해주어야 한다)

    2. 별도의 파라미터를 적지 않아도 IDE에서 표시를 해준다.(오른쪽 사진 밑줄 참조)

    3. 해당 파라미터의 이름을 작성해줄 수 있다.

    4. 파라미터가 여개 일 경우 이름의 순서를 바꾸어도 된다.

    5. JAVA처럼 오버라이드가 가능하다.

     

    위 내용을 활용해보자

    val day = randomDay()
    val food = fishFood(day)
    println("Today is $day and the fish eat $food")
    println("Change water: ${shouldChangeWater(day)}")
    
    // => Today is Thursday and the fish eat granules
    // Change water: false
    // => Today is Sunday and the fish eat plankton
    // Change water: true
    
    fun shouldChangeWater(day: String, temperature: Int = 22, dirty: Int = 20): Boolean {
        return when {
            temperature > 30 -> true
            dirty > 30 -> true
            day == "Sunday" -> true
            else -> false
        }
    }

    * 여기서 추가로 알 수 있는 게 나타났다

    shouldChangeWater함수의 첫 번째 파라미터만 입력하면 남은 두 개(temperature, dirty) 파라미터는 입력하지 않아도 된다.

    temperature, dirty의 파라미터는 이미 초기값이 선언되어 있기 때문에 선언되지 않은 첫 번째 파마리터만 호출 시 지정할 수 있다.

    하지만 중간에 선언되지 않은 경우 호출 시에는 전부 작성이 필요하다.

     

    shouldChangwWater함수를 조금 더 세분화시켜보자

    fun isTooHot(temperature: Int) = temperature > 30
    fun isDirty(dirty: Int) = dirty > 30
    fun isSunday(day: String) = day == "Sunday"
    
    fun shouldChangeWater(day: String, temperature: Int = 22, dirty: Int = 20): Boolean {
        return when {
            isTooHot(temperature) -> true
            isDirty(dirty) -> true
            isSunday(day) -> true
            else -> false
        }
    }

    * 함수 생성 시 기본값은 데이터 값이 아니어도 된다. 아래 코드(getDirtySensorReading)와 같이 함수를 사용하여도 된다.

    fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = getDirtySensorReading()): Boolean {
    ...

     

    * eager filter & lazy filter

    val decorations = listOf("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
    
    fun main() {
        println(decorations.filter { it[0] == 'p' })
    }
    
    // => [pagoda, plastic plant]

    list의 filter 함수를 사용하여 필요한 부분만 갖고 올 수 있다.(filter 뒤의 중괄호를 유심히 보자 람다식표현이다.)

     

    이제는 eager와 lazy에 대해서 알아보자

    eager를 번역하면 심한, 열심인, 간절히 바라는 이라는 뜻이다.

    eager filter는 기본적으로 간절히 바라고 있어 실행과 동시에 값이 리턴된다고 보면 될 것 같다. 

    lazy는 반대로 게으른, 여유로운 뜻으로, 실행하더라도 리턴되지 않는다.

    한번 예를 알아보자.

    val eager = decorations.filter { it [0] == 'p' }
    println("eager: " + eager)
    
    // lazy
    val filtered = decorations.asSequence().filter { it[0] == 'p' }
    println("filtered: " + filtered)
    
    // => eager: [pagoda, plastic plant]
    // filtered: kotlin.sequences.FilteringSequence@19469ea2

    eager는 출력과 동시에 값이 나오는 반면,

    lazy는 주소 값을 전달한다.(lazy를 사용하려 할 땐 asSequence()함수를 사용한다.)

    그래서 그 주소 값을 내용을 표시할 때 toList()함수를 사용하여 출력한다.

    println("new list: " + filtered.toList())
    
    // => new list: [pagoda, plastic plant]

    그리고 또 한 가지! lazy의 같은 경우 toList()등 결과를 출력 전 값의 변환이 발생되면 출력 값이 변경된다.

    // 수정할 수 있는 list로 변경
    val decorations = mutableListOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
    
    val eager = decorations.filter { it [0] == 'p' }
    println("eager: " + eager)
    
    val filtered = decorations.asSequence().filter { it[0] == 'p' }
    println("filtered: " + filtered)
    
    decorations.add("park") // 값 추가
    
    println("new list: " + filtered.toList())
    
    // => eager: [pagoda, plastic plant]
    // filtered: kotlin.sequences.FilteringSequence@19469ea2
    // new list: [pagoda, plastic plant, park]

    asSequence 함수를  코드를 살펴보면 Sequence 클레스와, iterator를 사용한다.

    그래서 iterator를 사용하는 map()도 lazy를 사용할 수 있다.

    val lazyMap = decorations.asSequence().map {
    	println("access: $it")
        it
    }
    
    println("lazy: $lazyMap")
    println("----")
    println("first: ${lazyMap.first()}")
    println("----")
    println("lazy: ${lazyMap.toList()}")
    
    // => lazy: kotlin.sequences.TransformingSequence@816f27d
    // ----
    // access: rock
    // first: rock
    // ----
    // access: rock
    // access: pagoda
    // access: plastic plant
    // access: alligator
    // access: flowerpot
    // lazy: [rock, pagoda, plastic plant, alligator, flowerpot]

    그리고 필터를 활용하여 map으로 만들 수 있다.

    val lazyMap2 = decorations.asSequence().filter { it[0] == 'p' }.map {
    	println("access: $it")
    	it
    }
    
    println("-----")
    println("filtered: ${lazyMap2.toList()}")
    
    // => -----
    // access: pagoda
    // access: plastic plant
    // filtered: [pagoda, plastic plant]

     

    * 고차 함수 (lamda표현)

    기본적인 함수를 사용할 수 있지만 코틀린에서는 람다를 지원한다.

    람다는 함수를 만드는 표현식이며 함수의 이름 없이 선언된다. 데이터를 만들 때 람다식 표현은 유용하게 사용이 된다. 다른 언어에서는 1급 함수, 익명함수, 리터럴 함수 등 표현된다.

     

    - 람다식 표현에 대해서 알아보자.

    위에서 filter작업중에 .filter { it[0] = 'p' } 를 보았을 것이다. 이것이 바로 고차 함수다. 비슷하게 위 map을 표현할 때도 사용되었다.

    filter, map에서 표현된 것은 파라미터를 받지 않던 함수 표현이다.

    파라미터를 받는 람다 표현식 표현을 사용할 수 있다. 중괄호의 시작 시 파라미터를 작성(타입지정 필수)하고 "->" 표시 후 오른쪽에 다음 코드를 작성할 수 있다. 

    람다가 변수에 할당되면 함수처럼 호출할 수 있다.

    var dirtyLevel = 20
    val waterFilter = { dirty : Int -> dirty / 2}
    
    print(waterFilter(dirtyLevel))
    // => 10

    int 타입으로 받으며, 값을 / 2 한다. 반환되는 값 또한 int이다.

    그래서 waterFilter에 별도의 타입은 지정하지 않았지만 자동 추론을 통해 int라는 것을 알 수 있다.

    코틀린의 함수 유형의 타입은 람다 구문과 밀접한 관련이 있다. 이 구문을 바꿔 보다 명확하게 표현이 가능하다.

    val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }

    전달받는 타입과, 리턴 타입을 명확하게 지정해주면 별도의 추론 없이도 유지보수에 도움이 될 수 있을 것 같다.

     

    - 고차 함수를 만들어 보자

    위에서 람다식 표현을 보면 대부분 함수처럼 보인다. 람다의 진정한 힘은 한 함수에 파라미터로 다른 함수를 사용하여 고차함수를 만드는 것이다.

    fun updateDirty(dirty: Int, operation: (Int) -> (Int)) : Int {
        return operation(dirty)
    }
    
    print(updateDirty(30, waterFilter))
    
    // => 15

    위 waterFilter의 람다 표현을 함수의 파라미터로 받고, 그 파라미터를 연산하여 리턴 값을 받게 되었다.

    :: 오퍼레이터 사용하면 람다식 표현이 아닌 일반 함수를 사용할 수 있다.

    fun increaseDirty(start: Int) = start + 1
    
    print(updateDirty(15, ::increaseDirty))
    // => 16

     

    코틀린은 함수의 모든 파라미터가 마지막 파라미터인 것을 선호한다.

    고차 함수로 작업할 때 마지막 파라미터를 호출하는 특수 구문을 사용하고 있어 코드를 간결하게 만들 수 있다.

     

    아래의 코드 같은 경우 함수의 파라미터를 람다로 전달 하지만 괄호 안에 넣을 필요는 없다.

    var dirtyLevel = 19
    dirtyLevel = updateDirty(dirtyLevel) { dirtyLevel -> dirtyLevel + 23 }
    print(dirtyLevel)
    
    // => 42

     

    코틀린

    'Android(+ Kotlin)' 카테고리의 다른 글

    [수정] ViewModel의 ViewModelProvider  (0) 2019.12.30
    MVVM 따라하기 Data Binding, LiveData (1)  (0) 2019.12.20
    [Kotlin]무작정 따라하기2  (0) 2019.11.08
    [Kotlin]무작정 따라하기 1  (0) 2019.10.16
    JAVA 용어 정리  (0) 2019.09.20

    댓글

Designed by Tistory.