-
[Kotlin]무작정 따라하기3Android(+ Kotlin) 2019. 11. 11. 19:36
무작정 따라 하기 2
https://codelabs.developers.google.com/codelabs/kotlin-bootcamp-functions/#1
부트캠프 기준으로 진행합니다.
이번 챕터에서는 상위함수&람다, 코틀린 코딩문법, 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