외부 반복과 아규먼트 매칭

·2021년 12월 6일
0
post-thumbnail

📌 범위와 반복


레인지 클래스

코틀린은 특정 범위의 값들을 반복하기 위해서 특화된 클래스들로 추상수준을 올렸다.

1~5까지 숫자의 범위를 이렇게 간단히 만들 수 있다

val onToFive: IntRange = 1..5

만약 알파벳의 범위를 사용하고 싶다면 동일한 방법으로 사용하면 된다

val aToE: CharRange = 'a'..'e'

range 클래스는 int,long,char 같은 원시타입에 국한되어있지 않다

val seekHelp: ClosedRange<String> = "hell".."help"

println(seekHelp.contains("helm")) //true
println(seekHelp.contains("helq")) //false

range는 범위의 첫 번째 값을 쓰고 .. 연산자를 붙이고 범위의 마지막 값을 적으면 된다.
range는 ..연산자의 앞, 뒤 두 값을 모두 포함한다.

정방향 반복

for(x in ..) 문번을 이용해 반복을 수행할 수 있다.

for (i in 1..5) { print("$i, ") } //1, 2, 3, 4, 5,
for (ch in 'a'..'e') { print(ch) } //abcde

하지만 "hell" 부터 "help"까지 반복을 시도하면 오류가 발생한다.

//ERROR : Kotlin: For-loop range must have an 'iterator()' method
for (word in "hell".."help") { print("$word, ") }

실행이 안되는 이유는 IntRange나 CharRange같은 클래스들은 iterator()함수가 있지만 ClosedRange<T>에는 iterator()함수가 없기 때문이다.

후방향 반복

  downTo()

후방향 반복은 downTo() 메소드를 사용하면 된다.

for (i in 5.downTo(1)) { print("$i, ") } //5, 4, 3, 2, 1,

//중위표기법을 사용하면 깔끔하다
for (i in 5 downTo 1) { print("$i, ") } //5, 4, 3, 2, 1, 

범위 안의 값 건너뛰기

  마지막값 건너뛰기 

 util()

숫자 범위를 반복할 때 마지막 값을 건너 뛰려면 util()을 이용하면 된다.

for (i in 1 until 5) { print("$i, ") } //1, 2, 3, 4,

이전 ..연산자와 달리 until은 마지막 값인 5를 포함시키지 않는다.

  중간 값 건너뛰기  

 step()

for (i in 1 until 10 step 3) { print("$i, ") } //1, 4, 7,

step() 메소드는 ". .",   "until",  "downTo" 등으로 만들어진 IntRange 또는 IntProgression 객체를 IntProgression 객체로 변화시킨다.

💡IntProgression객체는 일부 값을 건너뛸 수 있다.


for (i in 10 downTo 0 step 3) { print("$i, ")} //10, 7, 4, 1,

건너뛰면서 역방향하는 코드도 이런식으로 가능하다

 filter()

step()을 이용하면 순차적으로 값들을 건너뛴다. 순차적이지 않게 건너뛰고 싶다면 filter() 메소드를 이용하면 된다.

for (i in (1..9).filter { it % 3 == 0 || it % 5 == 0 }) {
    print("$i, ") //3, 5, 6, 9,
}

📌 배열과 리스트의 반복


 arrayOf()

배열을 만들기 위해서 kotlin의 arrayOf()함수를 사용한다.

val array = arrayOf(1, 2, 3)
for (e in array) { print("$e, ") }//1, 2, 3

 listOf()

비슷하게, listOf()를 이용해서도 만들 수 있다.

val list = listOf(1, 2, 3)
println(list.javaClass)// class java.util.Arrays$ArrayList
for (e in list) { print("$e, ") } //1, 2, 3,

 indices

배열의 인덱스 값을 알고싶다면 indices 속성을 이용한다.
인덱스를 이용해 해당 위치의 값을 알아냈다.

입력

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()

인덱스와 위치의 값을 한번에 얻는 방법도 있다.

입력

for ((index, name) in names.withIndex()) {
    println("Position of $name is $index")
}

💻 출력

Position of Tom is 0
Position of Jerry is 1
Position of Spike is 2

📌 when을 사용해야 할 때


kotlin에는 switch문이 없다. 대신에 when 이 있다.

표현식으로서의 when

아래의 함수는 콘웨이 생명게임에서 다음 세대 세포가 살아남을지 아닐지 결정하는 함수를 구현한 것이다.

fun isAlive(alive: Boolean, numberOfLiveNeighbors: Int): Boolean {
    if (numberOfLiveNeighbors < 2) {
        return false
    }
    if (numberOfLiveNeighbors > 3) {
        return false
    }
    if (numberOfLiveNeighbors == 3) {
        return true
    }
    return alive && numberOfLiveNeighbors == 2
}

함수가 너무 장황하고 지저분해 보인다

 when

when if-else문이나 표현식을 가장 간단한 형태로 바꿀 수 있다.

fun isAlive(alive: Boolean, numberOfLiveNeighbors: Int) = when {
    numberOfLiveNeighbors < 2 -> false
    numberOfLiveNeighbors > 3 -> false
    numberOfLiveNeighbors == 3 -> true
    else -> alive && numberOfLiveNeighbors == 2
}

리턴타입 추론을 이용하고 단일 표현식 함수 문법을 사용했다.
여기서 사용된 when은 표현식으로써 사용된 것이다. 함수에 의해 리턴되는 값은 when안의 하나의 표현식에서 나온 값이다.

입력

fun whatToDo(dayOfWeek: Any) = when (dayOfWeek) {
    "Saturday", "Sunday" -> "Relax"
    in listOf("Monday", "Tuesday", "Wednesday", "Tursday") -> "Work hard"
    in 2..4 -> "Work hard"
    "Friday" -> "Party"
    is String -> "What?"
    else -> "No clue"

}

println(whatToDo("Sunday"))
println(whatToDo("Wednesday"))
println(whatToDo(3))
println(whatToDo("Friday"))
println(whatToDo("Cat"))
println(whatToDo(8))

💻 출력

Relax
Work hard
Work hard
Party
What?
No clue

이번 예제는 when에 dayOfWeek라는 변수를 넘겼다. 이전 예제와 다르게 예제안의 조건들이 혼합되어 있다.

 ✔   when 안의 첫 번째 라인은 전달받은 값을 콤마로 구분된 두 개의 값 중 하나에 해당하는지 확인한다.
 ✔   전달 받은 값이 리스트나 범위에 있는지를 확인하려면 타입체크도 해야한다.
 ✔   is String으로 시작된 라인에서 하는것이 타입체크이다.
 ✔   마지막으로 else문은 기본 선택지를 담당한다.

위의 예제같은 경우에는 else가 필수적으로 있어야한다. 코틀린 컴파일러는 else가 마지막이 아닌 부분에 오는 것은 허용하지 않는다.

위의 예제에서는 -> 다음에 단일 표현식만 오기 때문에 블록을 쓰지 않았지만 블록을 사용하는 것도 허용한다.
하지만 가독성 측면으로 봤을 때 블록은 이용하지 않는것이 좋다.

명령문으로서의 when

이전 코드와 다르게 스트링을 리턴하지말고 출력한다.

입력

fun printWhatToDo(dayOfWeek: Any) {
    when (dayOfWeek) {
        "Saturday", "Sunday" -> println("Relax")
        in listOf("Monday", "Tuesday", "Wednesday", "Tursday") -> println("Work hard")
        in 2..4 -> println("Work hard")
        "Friday" -> println("Party")
        is String -> println("What?")
    }
}

printWhatToDo("Sunday")
printWhatToDo("Wednesday")
printWhatToDo(3)
printWhatToDo("Friday")
printWhatToDo("Cat")
printWhatToDo(8)

💻 출력

Relax
Work hard
Work hard
Party
What?

printWhatToDo() 함수의 리턴값은 Unit이다. 아무것도 리턴하지 않는다.
when이 명령문으로 사용될 때는 else가 없어도 상관없다.
printWhatToDo(8)을 보면 알 수 있듯이 적합한 조건이 없다면 아무런 행동도 하지 않는다.

when과 변수의 스코프

이전 예제를 보면 매칭에 사용된 변수는 when의 스코프만으로 제한되어있다. 저런 제약이 함께하는 코드의 디자인은 아주 적절하다. 왜냐면 변수가 새어나가서 다르게 사용되는 것을 방지할 수 있고 코드를 관리하기 쉽게 만들어준다.

입력

fun systemInfo(): String {
    //코어숫자파악
    val numberOfCores = Runtime.getRuntime().availableProcessors()
    return when (numberOfCores) {
        1 -> "1 core, packing this one to the museum"
        in 2..16 -> "You have $numberOfCores cores"
        else -> "$numberOfCores cores!, I want your machine"
    }
}
systemInfo()

💻 출력

You have 12 cores

systemtInfo() 함수는 코어의 숫자를 리턴한다.
Runtime의 availableProcessors() 메소드를 이용해서 코어의 숫자를 파악한 후 코어의 숫자를 담은 변수를 when에 전달하고 있다.

fun systemInfo(): String =
    when (val numberOfCores = Runtime.getRuntime().availableProcessors()) {
        1 -> "1 core, packing this one to the museum"
        in 2..16 -> "You have $numberOfCores cores"
        else -> "$numberOfCores cores!, I want your machine"
    }

코드에서 val을 when의 인자로 넣어버리면 약간의 이득을 얻을 수 있다.

  1. when 결과를 바로 리턴하여 바깥쪽 {}블럭과 return 키워드를 제거해 덜 복잡한 짧은 코드를 만들 수 있다.
  2. numberOfCores은 when의 결과를 얻을 때만 사용 가능하고, 이후의 연산에서는 사용이 불가능하다.

변수의 스코프를 제한하는 것은 좋은 디자인이다



🔑 정리


코틀린의 명령형 반복은 다른 주요 언어의 반복보다 더 풍부한 기능과 표현을 제공한다.

코틀린의 특별한 클래스인 범위를 이용해서 정방향/역방향 반복, 스킵 등을 쉽게 만들어 준다.

when이 만들어주는 전달인자 매칭분법이 기존의 조건문을 사용할 때 딸려오는 코드의 노이즈를 제거해준다.



출처 : 다재다능 코틀린 프로그래밍

profile
개발하고싶은사람

0개의 댓글