Study/다재다능 코틀린 프로그래밍

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

주지민 2021. 10. 27. 00:05
반응형

본 포스트는 지인들과 스터디한 내용을 정리한 포스트입니다

 

http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788931463422

 

다재다능 코틀린 프로그래밍 - 교보문고

다양한 프로그래밍 패러다임으로 살펴보는 코틀린 기본서 | 코틀린은 멀티패러다임 프로그래밍 언어다. 코틀린은 스크립트로 사용할 수도 있고, 객체지향 코드나 함수형 코드, 비동기 프로그램

www.kyobobook.co.kr


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

  • 코틀린은 명령형 스타일에서 사용되는 외부 반복자와 함수형 스타일에서 사용되는 내부 반복자 모두 제공
  • 현재 챕터에서는 외부 반복의 효율성에 대해서 집중
  • kotlin.ranges 패키지는 반복을 편하게 해주는 심플한 코드를 제공
  • 레인지 클래스
    • 범위의 첫 번째 값을 쓰고 '..'연산자를 붙이고 범위의 마지막 값을 적으면 된다
    • range는 연산자의 앞, 뒤 두 값 모두를 포함한다
    • 예제 코드
      val oneToFive: IntRange = 1..5 
      val aToE: CharRange = 'a'..'e' 
      val seekHelp: ClosedRange<String> = "hell".."help"​
  • 정방향 반복
    • for(x in ..) 문법을 통해 생성된 범위안에서 반복을 수행할 수 있다 ( 오름차순으로 진행되는 반복 )
    • 예제 코드
      // ranges.kts
      for (i in 1..5) { print("$i, ") // 1,2,3,4,5, }​​
    • 명시적으로 선언하지 않아도 'i'는 val이다 ( 이뮤터블 ), 또한 반복문 안으로 스코프가 제한된다
    • 위처럼 외부 반복을 하기위한 조건은 생성된 범위 객체가 iterator() 함수가 있어야한다
      • IntRange, CharRange 같은 클래스는 iterator() 함수(연산자)가 존재
      • ClosedRange에는 iterator() 함수가 존재하지 않는다
        error: for-loop range must have an 'iterator()' method
  • 후방향 반복
    • 내림차순으로 진행되는 반복
    • "5..1"과 같은 형태로 만들면 되지 않을까 생각할 수 있지만 아쉽게도 .downTo() 메서드를 사용하여 내림차순을 나타줘야한다
    • 예제코드
      // reverse.kts 
      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, 
      }​

      • 시작값.downTo(끝값) => 시작값을 포함하여 끝값을 포함한 값까지 내림차순으로 반복
      • 시작값 downTo 끝값 => 훨씬더 간략하게 표현할 수 있다
  • 범위 안의 값 건너뛰기
    • 지금까지는 순차적인 값의 증가, 값의 감소를 반복시켰지만 범위 안에서 특정 값을 건너뛰는 방법을 소개한다
    • until() 메서드
      • 숫자 범위를 반복할 때 마지막 값을 건너뜀 ( 마지막 값을 포함하지 않음 )
      • downTo처럼 .메서드 로 표현할 수 있지만 간결하게 .과 괄호를 버릴 수 있다
      • 예제코드
        // skipvalues.kts 
        for (i in 1.until(5)) { 
        	print("$i, ") // 1,2,3,4, 
        } 
        
        for (i in 1 until 5) { 
        	print("$i, ") // 1,2,3,4, 
        }​​
    • step() 메서드
      • 반복 중에 특정 값을 건너뛸때 사용
      • "..", "until", "downTo" 등으로 만들어진 IntRange, IntProgression 객체를 IntProgression 객체로 변화시킨다
      • IntProgression 객체는 일부 값을 건너 뛴다
      • 예제코드
        // skipvalues.kts 
        for (i in 1 until 10 step 3) { 
        	print("$i, ") // 1, 4, 7 
        }​
    • filter() 메서드
      • step() 메서드가 순차적인 방법으로 값을 건너뛴 함수라면 filter() 메서드는 값을 건너뛸 조건 자체를 만드는 함수이다
      • 예제코드
        // skipvalues.kts 
        
        // case1: simple case 
        for (i in (1..9).filter { it % 3 == 0 || it % 5 == 0 }) { 
        	print("$i, ") // 3, 5, 6, 9 
        }
        
        // case2: Using lambda
        val myCondition = { it: Int -> it % 3 == 0 }
        
        for (i in (1..5).filter(myCondition)) {
        	print("$i, ")
        }

 

2. 배열과 리스트의 반복

  • 코틀린에서는 특별한 조치 없이 JDK의 콜렉션 클래스와 인터페이스를 바로 사용할 수 있다
  • 예제코드 ( 배열 생성 )
    // iterate.kts
    val array = arrayOf(1,2,3)​

    • 배열을 만들기위해 arrayOf() 메서드 사용
    • kotlin 패키지에 포함된 함수는 굳이 kotlin.arrayOf()라고 적거나 kotlin을 임포트할 필요없이 사용할 수 있다
    • primitive type의 배열로 만들려면 type(int 등) prefix를 가진 intArrayOf(...) 메서드를 사용하면 된다
  • 예제코드 ( 리스트 컬렉션 생성 )
    // iterate.kts
    val list = listOf(1,2,3)
    println(list.javaClass) // class java.util.Arrays$ArrayList
    for (e in list) {
        print("$e, ") // 1, 2, 3,
    }​
    • 콜렉션의 값을 이용하여 출력한 형태이다
    • list 안의 인덱스를 찾고 싶다면 indices 속성을 사용하면 된다
    • 예제 코드
      // index.kts
      var 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
      
      
      for ((index, name) in names.withIndex()) {
          println("Position of $name is $index")
      }
    • withIndex() 메서드를 통해 인덱스와 값 둘 다 한꺼번에 가져올 수도 있다

 

3. when을 사용해야 할 때

  • 코틀린에는 switch문이 없는 대신 when이 있다
  • 표현식으로서의 when
    • 예제코드
      // when.kts
      fun beforeIsAlive(alive: Boolean, numberOfLiveNeighbors: Int): Boolean {
          if (numberOfLiveNeighbors < 2) {
              return false
          }
      
          if (numberOfLiveNeighbors > 3) {
              return false
          }
      
          if (numberOfLiveNeighbors == 3) {
              return true
          }
          return alive && numberOfLiveNeighbors == 2
      }
      
      fun isAlive(alive: Boolean, numberOfLiveNeighbors: Int) = when {
          numberOfLiveNeighbors < 2 -> false
          numberOfLiveNeighbors > 3 -> false
          numberOfLiveNeighbors == 3 -> true
          else -> alive && numberOfLiveNeighbors == 2
      }​

      • beforeIsAlive 메서드를 살펴보면 전통적으로 사용하던 if-else문의 조합으로 구성되어 있다, 지저분하고 장황하며 오류를 유발한다
      • when을 이용한 isAlive 메서드는 리턴타입을 타입추론을 통해 이용하게 하고 단일 표현식 함수 문법을 사용했다
      • 즉 함수에 의해서 리턴되는 값은 when 안의 하나의 표현식에서 나온 값이다
    • when에 값이나 표현식을 전달
      • 예제코드
        // activity.kts
        fun whatToDo(dayOfWeek: Any) = when (dayOfWeek) {
            "Saturday", "Sunday" -> "Relax"
            in listOf("Monday", "TuesDay", "Wednesday", "Thursday") -> "Work hard"
            in 2..4 -> "Work hard"
            "Fridy" -> "Party"
            is String -> "What?"
            else -> "No clue"
        }
        
        println(whatToDo("Sunday")) // Relax
        println(whatToDo("Wednesday")) // Work hard​

        • 위처럼 when 파라미터로 값을 전달하여 심플하게 구성할 수 있다
        • 각 case에는 두 개 이상의 값을 사용하는 것이 허용된다
      • 코틀린 컴파일러는 when에서 else가 마지막이 아닌 부분에 오는 것을 허용하지 않는다
  • 명령문으로써의 when
    • 하나 이상의 값에 기반해서 다른 동작을 원한다면 when을 표현식이 아니라 명령문으로 사용하면 된다
    • 예제코드
      // activity.kts
      fun whatToDo(dayOfWeek: Any) {
          when (dayOfWeek) {
              "Saturday", "Sunday" -> println("Relax")
              in listOf("Monday", "TuesDay", "Wednesday", "Thursday") -> println("Work hard")
              in 2..4 -> println("Work hard")
              "Fridy" -> println("Party")
              is String -> println("What?")
              else -> println("No clue")
          }
      }
      
      whatToDo("Sunday")
      whatToDo("Wednesday")
      whatToDo(3)​

      • 해당 함수 whatToDo()의 리턴타입은 Unit이다, 아무것도 리턴하지 않고 println()만 호출
  • when과 변수의 스코프
    • 이전 예제들에서 변수는 when 명령문 혹은 표현식의 외부에서 전달받아 매칭을 위해서만 사용되었다
    • 매칭에 사용되는 변수는 when의 스코프만으로 제한되어있다
    • 예제코드: 전통적인 방식의 장황한 코드
      // cores.kts
      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"
          }
      }​

      • val numberOfCores 는 결국 when 스코프안에서만 쓰이는 변수이다
    • 예제코드: when 파라미터로 전달
      // cores.kts
      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"
          }
      
      println(systemInfo())
       
      • when 파라미터로 해당 블록안에서만 사용할 변수를 선언 및 초기화해줬다
      • when의 결과를 바로 리턴하여 systemInfo() 메서드를 감싸고있던 {} 블록과 return 키워드가 없어졌다
728x90
반응형