본문 바로가기

스터디/다재다능 코틀린 프로그래밍

4. 외부 반복과 아규먼트 매칭

다재다능 코틀린 프로그래밍 책으로 코틀린 스터디를 진행하면서 발표를 위해 준비했던 글입니다.


범위와 반복

레인지 클래스

  • 1 ~ 5 까지 숫자의 범위 만들기
val oneToFive: IntRange = 1..5
// IntRange 타입이 명확성을 제공해주지만 IntRange를 적지 않아도 타입추론이 가능하다.
  • a ~ e 까지 알바벳 만들기
val aToE: CharRange = 'a'..'e'
  • 문자열 범위 만들기
val seekHelp: ClosedRange<String> = "hell".."help"
// hell, helm, heln, help

정방향 반복

  • 1 ~ 5 반복
for (i in 1..5) { print("$i, ")} // 1, 2, 3, 4 ,5
// 명시적으로 선언하지 않아도 변수 i는 val → 안정적이다.
  • a ~ e 반복
for (ch in 'a'..'e') { print(ch) } // abcde
  • hell ~ help 반복
for (word in "hell".."help") { print("$word, ") } // ERROR
// IntRange나 CharRange 같은 클래스들은 iterator()함수가 있는데,
// ClosedRange<T>에는 iterator()가 없기 때문, 방법은 있다. 12-2에서 설명할 예정

후방향 반복

  • downTo() 메소드를 사용하여 후방향 반복을 쉽게 할 수 있다.
  • 5 ~ 1 감소하는 반복
for (i in 5.downTo(1)) { print("$i, ") } // 5, 4, 3, 2, 1
// downTo() 메소드 호출 시 IntProgression 인스턴스를 리턴한다.
// IntProgression은 kotlin.rangs 패키지의 일부
// 12-4에서 코드를 더 간단하게 만들 수 있는 방법을 설명할 예정
  • 5 ~ 1 감소하는 반복 개선
for (i in 5 downTo 1) { print("$i, ") } // 5, 4, 3, 2, 1

범위 안의 값 건너 뛰기

  • until() 메소드를 사용해서 마지막 값을 포함시키지 않을 수 있다.
for (i in 1 until 5) { print("$i, ") } // 1, 2, 3, 4, 
  • 값을 건너뛸 때 i = i + 3 같은 식은 코틀린에서는 불가능, i 가 기본적으로 val 이기 때문
  • step() 메소드를 사용하여 값을 건너뛸 수 있다.
for (i in 1 until 10 step 3) { print("$i, ") } // 1, 4, 7, 
  • 역방향 반복 값 건너 뛰기
for (i in 10 downTo 0 step 3) { print("$i, ") } // 10, 7, 4, 1
  • filter() 메소드를 사용해 순차적이지 않은 값을 건너뛸 수 있다.
  • 3의 배수나 5의 배수만 반복하기
for (i in (1..9).filter { if % 3 == 0 || it % 5 == 0 }) {
    print("$i, ") // 3, 5, 6, 9
}

배열과 리스트의 반복

  • arrayOf() 함수를 사용해 배열을 만들 수 있다.
val array = arrayOf(1, 2, 3)
for (e in array) { print("$e, ") }
  • 원시 자료형인 Int 배열을 생성하기 위해선 intArrayOf() 메소드를 사용하면 된다.
  • listOf() 메서드를 사용해서 List를 만들 수 있다.
val list = listOf(1, 2, 3)
println(list.javaClass) // ArrayList
for (e in list) { print("$e, ") } // 1, 2, 3
  • 인덱스가 필요할때의 반복문
val names = listOf("Tom", "Jerry", "Spike")
for (index in names.indices) {
    println("Position of ${names.get(index)} is $index")
}
// Position of Tom is 0
// Position of Jerry is 1
// Position of Spike is 2
  • withIndex() 함수를 사용해서 인덱스와 value 얻기
val names = listOf("Tom", "Jerry", "Spike")
for ((index, name) in names.withIndex()) {
    println("Position of $name is $index")
}

when을 사용해야 할 때

  • 코틀린에는 switch 대신 when이 있다.

표현식으로서의 when

fun isAlive(alive: Boolean, numberOfLiveNeighbors: Int) = when {
    numberOfLiveNeighbors < 2 -> false
    numberOfLiveNeighbors > 3 -> false
    numberOfLiveNeighbors == 3 -> true
    else -> alive &&     numberOfLiveNeighbors == 2
}
  • 단일 표현식 함수 문법 사용 → 리턴 타입 추론
  • when은 if에 비해서 간결하다.
  • when에서 모든 입력에 대한 값을 생성하는지 또는 else가 있는지 컴파일 타임 체크를 해준다.
  • Any 타입 파라미터를 받는 메소드에서 when에 값이나 표현식 전달하기
fun whatToDo(dayOfWeek: Any) = when (dayOfWeek) {
    "Saturday", "Sunday" -> "Relax"
    in listOf("Monday", "Tuesday", "Wednesday" "Thursday") -> "Work hard"
    in 2..4 -> "Work hard"
  "Friday" -> "Party"
    is String -> "What?"
    else -> "No clue"
}
println(whatToDo("Sunday")) // Relaxy
println(whatToDo("Wednesday")) // Work hard
println(whatToDo(3)) // Work hard
println(whatToDo("Friday")) // Party
println(whatToDo("Munday")) // What?
println(whatToDo(8)) // No clue
  • when에서 else가 마지막이 아닌 다른 곳에 오는 것은 허용하지 않는다.
  • → 뒤에 블록{}이 오는 것이 허용되지만 가독성 측면에서는 이용하지 않는게 좋다. → 단일 표현식에 메소드를 호출하는 방식으로 사용하자.

명령문으로써의 when

fun printWhatToDo(dayOfWeek: Any) {
    when (dayOfWeek) {
        "Saturday", "Sunday" -> println("Relax")
        in listOf("Monday", "Tuesday", "Wednesday" "Thursday") -> println("Work hard")
        in 2..4 -> println("Work hard")
      "Friday" ->  println("Party")
        is String -> println("What?")
}
printWhatToDo("Sunday") // Relaxy
printWhatToDo("Wednesday") // Work hard
printWhatToDo(3) // Work hard
printWhatToDo("Friday") // Party
printWhatToDo("Munday") // What?
printWhatToDo(8) //
  • else가 없어도 상관 없는 이유: 리턴타입이 Unit이기 때문
  • 즉 when이 명령문으로 사용될 때는 else가 없어도 상관없다.

when과 변수의 스코프

  • val을 분리
fun systemInfo(): String {
    val numberOfCores = Ruintime.getRuntime().availableProcessors()
    return when (numberOfCores) {
        1 -> "1 core, ..."
        in 2..15 -> "You have $numberofCores cores"
        else -> "$numberOfCores cores!..."
    }
}
  • val을 when 인자로 넣기
fun systemInfo(): String =
    return when (val numberOfCores = Ruintime.getRuntime().availableProcessors()) {
        1 -> "1 core, ..."
        in 2..15 -> "You have $numberofCores cores"
        else -> "$numberOfCores cores!..."
}
  • val을 when 인자로 넣으면 짧은 코드를 만들 수 있다.
  • numberOfCores는 when의 결과를 얻을 때만 사용가능하고 이후 연산에서는 사용이 불가능
  • 변수의 스코프를 제한하는 것은 좋은 디자인