본문 바로가기
대외활동/Naver Boostcourse

[부스트코스] 코틀린 프로그래밍 기본 1/2(함수편) - 프로그램 흐름 제어

by 드인 2021. 2. 12.

코틀린 프로그래밍 기본 1/2 (함수편)

 


 

0. 오리엔테이션

1. 코틀린의 기본의 기본을 읽혀요!

- 코틀린이란 무엇일까?

 - 변수와 자료형, 연산자

2. 함수형 프로그래밍 이란?

 - 마법의 요술상자, 함수의 기본

 - 요술상자, 함수 가지고 놀기

3. 프로그램 흐름의 제어

 - 프로그램의 흐름을 제어해보자!

4. 코틀린의 표준함수 활용하기

 - 코틀린과 표준함수

5. 강좌 마무리 프로젝트

▶ 깃허브 코드 : github.com/0525hhgus/Kotlin-study

 

0525hhgus/Kotlin-study

Kotlin-study. Contribute to 0525hhgus/Kotlin-study development by creating an account on GitHub.

github.com


[ 프로그램 흐름 제어]


1. 조건문을 통한 분기

1) if문과 if~else문

▶ if문

if (조건식) { 
    수행할 문장 // 조건식이 참인 경우에만 수행
    ...
}
// if 문
var max = 0
if (a < b)
	max = b // 수행할 문장이 한 줄이면 중괄호를 생략할 수 있음

 

▶ if~else문

if (조건식) { 
    수행할 문장 // 조건식이 참인 경우에만 수행
} else { 
    수행할 문장 // 조건식이 거짓인 경우에 수행
}

 

▶ if~else문의 간략화

var max: Int
if (a > b)
	max = a
else
	max = b
val max = if (a > b) a else b

 

▶ 블록과 함께 사용하는 경우

fun main() {
	val a = 12
    val b = 7
    
    // 블록과 함께 사용
    val max = if (a > b) {
    	println("a 선택")
        a // 마지막 식인 a가 반환되어 max에 할당
    }
    else {
    	println("b 선택")
        b // 마지막 식인 b가 반환되어 max에 할당
    }
    
    println(max)
}

- 반환된다고 해서 return을 사용하는 것은 아님

 

▶ 실습: if~else문 사용해보기

package chap05.section1

fun main(){
    val a = 17
    val b = 7

    val max = if (a > b) {
        println("a $a")
        a
    } else {
        println("a $b")
        b
    }

    println("max $max")
}

 

 

2) else if 다중 조건

▶ else if는 필요한 만큼 조합할 수 있음

- 마지막은 else로 구성

val number = 0 
val result = if (number > 0) 
        "양수 값" 
    else if (number < 0) 
        "음수 값" 
    else 
        "0" 

 

▶ 실습: else if 성적 등급 출력하기

package chap05.section1

fun main() {

    print("Enter the score: ")
    val score = readLine()!!.toDouble()
    var grade: Char = 'F'

    if (score >= 90.0) {
        grade = 'A'
    } else if (score >= 80 && score <= 89.9) {
        grade = 'B'
    } else if (score >= 70 && score <= 79.9) {
        grade = 'C'
    }

    println("score: $score, grade: $grade")
}

 

 

3) in 연산자와 범위 연산자

▶ 비교 연산자와 논리 연산자의 복합

- ...} else if (score >= 80.0 && score <= 89.9) {...

- 비교 연산자(>=, <=)와 논리곱 연산자(&&)가 사용

 

▶ 범위(range) 연산자

- 변수명 in 시작값..마지막값

- score in 80..89이면 score 범위에 80부터 89까지 포함

 

▶ 실습: in 연산자와 범위 연산자 적용하기

package chap05.section1

fun main() {

    print("Enter the score: ")
    val score = readLine()!!.toDouble()
    var grade: Char = 'F'

    if (score >= 90.0) {
        grade = 'A'
    } else if (score in 80.0..89.9) {
        grade = 'B'
    } else if (score in 70.0..79.9) {
        grade = 'C'
    }

    println("score: $score, grade: $grade")
}

 

4) when문을 사용한 다양한 조건 처리

▶ 인자를 사용하는 when문

when (인자) {
  인자에 일치하는 값 혹은 표현식 -> 수행할 문장
  인자에 일치하는 범위 -> 수행할 문장
  ...
  else -> 문장
}
when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 블록 구문 사용 가능
        print("x는 1, 2가 아닙니다.")
    }
}

 

- 일치되는 여러 조건

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("기타")
}

- 함수의 반환값 사용하기

when (x) {
    parseInt(s) -> print("일치함!")
    else -> print("기타")
}

 

5) when과 일치되는 여러 조건과 함수 사용

package chap04.section1

fun main() {

    val nestedLambda: ()->()->Unit = { { println("nestedLambda") } }
}

 

6) when과 in, is 연산자 사용

▶ in 연산자와 범위 지정자 사용

when(x) {
	in 1..10 -> print("x는 1 이상 10 이하입니다.")
    !in 10..20 -> print("x는 10 이상 20 이하의 범위에 포함되지 않습니다.")
    else -> print("x는 어떤 범위에도 없습니다.")
}

 

▶ is 키워드 함께 사용하기

val str = "안녕하세요."
val result = when(str) {
	is String -> "문자열입니다."
    else -> false
}

 

▶ 실습: when 구문을 사용한 등급

package chap05.section1

fun main() {

    print("Enter the score: ")
    val score = readLine()!!.toDouble()
    var grade: Char = 'F'

    when (score) {
        in 90.0..100.0 -> grade = 'A'
        in 80.0..80.9 -> grade = 'B'
        in 70.0..70.9 -> grade = 'C'

    }
    
// ctrl + shift + / -> /* */ 주석 처리
/*    if (score >= 90.0) {
        grade = 'A'
    } else if (score in 80.0..89.9) {
        grade = 'B'
    } else if (score in 70.0..79.9) {
        grade = 'C'
    }*/

    println("score: $score, grade: $grade")
}

 

7) 인자가 없는 when

▶ 특정 인자에 제한하지 않고 다양한 조건을 구성

when {
  조건[혹은 표현식] -> 실행문
  ...
}

 

▶ 인자가 없는 when의 예

fun main() {
	print("Enter the score:")
    var score = readLine()!!.toDouble()
    var grade: Char = 'F'
    
    // 인수 없는 when의 사용
    when {
    	score >= 90.0 -> grade = 'A' // 인자 있는 when과 다르게 조건식을 구성할 수 있음
        score in 80.0..89.9 -> grade = 'B'
        score in 70.0..79.9 -> grade = 'C'
        score < 70.0 -> grade = 'F'
    }
    println("Score: $score, Grade: $grade")
}

 

 

🤔 생각해보세요.

 when구문의 조건에는 함수, 콤마로 구분한 여러개의 값 등 여러가지가 사용될 수 있습니다. 다양한 조건을 필요로 하는 문제를 하나 생각해서 when구문으로 구성해보세요.

Q. 다양한 조건을 필요로 하는 문제를 하나 생각해서 when구문으로 구성

A. 물품별로 할인된 가격을 출력하는 프로그램

- 입력 : 물품 이름

- 조건 : 가격이 '100 이상 200 이하'이면 10% 할인된 가격으로

- 출력 : 할인된 가격

package chap05.section1

fun main() {

    print("Enter the item:")
    val item = readLine()
    var price: Double = 0.0

    if (item.equals("apple")) {
        price = 100.0
    } else if (item.equals("orange")) {
        price = 120.0
    } else if (item.equals("melon")) {
        price = 50.0
    }

    when (price) {
        in 100.0..200.0 -> price *= 1.1
    }

    println("item: $item, price: $price")
}

 

2. 반복문

1) for문

▶ for문의 선언

for (요소 변수 in 컬렉션 혹은 범위) { 
	반복할 본문 
}
for (x in 1..5) { // 코틀린의 in과 범위 지정을 활용한 루프
  println(x) // 본문
}

for (x in 1..5) println(x) // 한줄에 표현하는 경우

- 코틀린에서는 자바와 같은 세미콜론 표현식을 사용하지 않음

 

▶ for문 예제

- for문을 사용해 1부터 10까지 더하기

fun main() {
	var sum = 0
    
    for (x in 1..10) sum += x
    
    println("sum: $sum")
}

 

▶ 하행 반복 - downTo

- 5, 4, 3, 2, 1

for (i in 5 downTo 1) print(i)
for (i in 5..1) print(i) // 잘못된 사용! 아무것도 출력되지 않는다

 

▶ 필요한 단계 증가 - step

- 1, 3, 5

for (i in 1..5 step 2) print(i)

 

▶ 혼합 사용

- 5, 3, 1

for (i in 5 downTo 1 step 2) print(i)

 

▶ 실습: for문의 예

package chap05.section2

fun main() {

    var sum = 0

    for (num in 1..100 step 2) { // 홀수합
        sum += num
    }

    println("sum: $sum")
}
package chap05.section2

fun main() {

    var sum = 0

    for (num in 0..99 step 2) { // 짝수합
        sum += num
    }

    println("sum: $sum")
}

 

2) while문

▶ while문 선언

while (조건식) { // 조건식이 true인 동안 본문의 무한 반복
    본문
    ....
}
var i = 1
while (i <= 5) {
	println("$i")
    ++i // 계속 반복하다 보면 조건식이 5 이상으로 넘어갈 때 false가 되어 탈출
}

- 데몬 프로그램의 사용 예

while (true) {
	temp = 온도 검사
    if (temp > 한계 온도) { 경고 발생 }
    ...
}

 

▶ while문 예제

- while문을 활용한 팩토리얼 예

fun man() {
	print("Enter the number: ")
    var number = readLine()!!.toInt()
    var factorial: Long = 1
    
    while (number > 0) { // n x ... x 4 x 3 x 2 x 1
    	factorial *= number
        --number
    }
    
    println("Factorial: $factorial")
}

 

3) do~while문

▶ do~while문 선언

do {
  본문
} while (조건식)

 

▶ 실습: do~while문을 활용한 순환문자열

package chap05.section2

// 목표 : 5 -> 12345, 23451, 34512 ...
fun main() {

    do {
        print("Enter the number: ")
        val input = readLine()!!.toInt()

        for (i in 0 until input) { // == for (i in 0..(input - 1))
            for (j in 0 until input) {
                print((i + j) % input + 1)
            }
            println()
        }

    } while(input != 0)

}

 

🤔 생각해보세요.

 반복문에 사용하는 for, while, do~while등을 조합하면 다양한 코드의 흐름을 구성할 수 있게 됩니다. 그렇다면 for문을 이용해 입력받은 줄 수만큼 *기호로 삼각형을 만들어내기 위해서는 어떻게 하면 될까요?

다음은 출력의 예 입니다. 

Enter the lines: 5
    *
   ***
  *****
 *******
*********

Q. for문을 이용해 입력받은 줄 수만큼 *기호로 삼각형을 만드는 프로그램

A. Hint

n: 줄 수 입력
반복 (line: 1 → n만큼) {
    반복 (space: 1 → (n-line)만큼) { 공백 출력 }
    반복 (star: 1 → (2*line-1)만큼) { 별표 출력 }
    개행
}
package chap05.section2

fun main() {

    print("Enter the line:")

    val n = readLine()!!.toInt()

    for ( line in 1..n ) {
        for ( space in 1..(n - line) )
            print(" ")

        for ( star in 1 until 2 * line ) // == for ( star in 1..(2 * line - 1) )
            print("*")
        println()
    }
}

 

3. 흐름의 중단과 반환

1) 흐름 제어 관련 요약

▶ 흐름 제어문

- return: 함수에서 결괏값을 반환하거나 지정된 라벨로 이동

- break: for이나 while의 조건식에 상관없이 반복문을 끝냄

- continue: for이나 while의 반복문의 본문을 모두 수행하지 않고 다시 조건으로 넘어감

 

▶ 예외 처리문

- try { ... } catch { ... }: try 블록의 본문을 수행하는 도중 예외가 발생하면 catch 블록의 본문을 실행

- try { ... } catch { ... } finally { ... }: 예외가 발생해도 finally 블록 본문은 항상 실행

 

2) return

▶ return으로 값 반환하기

fun add(a: Int, b: Int): Int P
	return a + b
    println("이 코드는 실행되지 않습니다.") // 여기까지 도달하지 않음
}

▶ return으로 Unit 반환하기

// 1. Unit을 명시적으로 반환
fun hello(name: String): Unit {
	println(name)
    return Unit
}
// 2. Unit 이름을 생략한 반환
fun hello(name: String): Unit {
	println(name)
    return
}
// 3. return문 자체를 생략
fun hello(name: String): Unit {
	println(name)
}

 

람다식에서 리턴 사용의 예

fun main() {
	retFunc()
}

inline fun inlineLambda(a: Int, b: Int, out: (Int, Int) -> Unit) {
	out(a, b)
}

fun retFunc() {
    println("start of retFunc") // ①
    inlineLambda(13, 3) { a, b ->  // ②
        val result = a + b
        if(result > 10) return // ③ 10보다 크면 이 함수를 빠져 나감, 비지역 반환
        println("result: $result") //  ④ 10보다 크면 이 문장에 도달하지 못함 
    }
    println("end of retFunc") 
}

 

3) 라벨을 사용한 return

람다식에서 라벨 사용

람다식 함수명 라벨이름@ {
	...
    return@라벨이름
}
fun inlineLambda(a: Int, b: Int, out: (Int, Int) -> Unit) { // inline이 제거됨
    out(a, b)
}

fun retFunc() {
    println("start of retFunc")
    inlineLambda(13, 3) lit@{ a, b ->  // ① 람다식 블록의 시작 부분에 라벨을 지정함
        val result = a + b
        if(result > 10) return@lit // ② 라벨을 사용한 블록의 끝부분으로 반환
        println("result: $result")
    } // ③ 이 부분으로 빠져나간다
    println("end of retFunc") //  ④ 이 부분이 실행됨 
}

 

 실습: 람다식 리턴과 라벨 사용 예

package chap05.section3

fun main() {
    retFunc()
}

inline fun inlineLambda(a: Int, b: Int, out: (Int, Int) -> Unit) {
    out(a, b)
}

fun retFunc() {
    println("Start of Func")
    inlineLambda(12,3) lit@{ a, b ->
        val result = a + b
        if (result > 10) return@lit
        println("result: $result")
    }
    println("end of Func")
}

 

▶ 암묵적 라벨

...
fun retFunc() {
    println("start of retFunc")
    inlineLambda(13, 3) { a, b -> 
        val result = a + b
        if(result > 10) return@inlineLambda 
        println("result: $result")
    } 
    println("end of retFunc") 
}
...

 

▶ 익명함수를 사용한 반환

fun retFunc() {
    println("start of retFunc")
    inlineLambda(13, 3, fun (a, b) { // 일반 익명함수 -> 비지역 반환
        val result = a + b
        if(result > 10) return
        println("result: $result")
    } // inlineLambda() 함수의 끝
    println("end of retFunc") 
}

 

4) 람다식 방법과 익명 함수 비교

▶ 람다식 방법

val getMessage = lambda@ { num: Int ->
    if(num !in 1..100) {
        return@lambda "Error" // 레이블을 통한 반환
    }
    "Success" // 마지막 식이 반환
}

 

▶ 익명 함수 방법

val getMessage = fun(num: Int): String {
    if(num !in 1..100) {
        return "Error"
    }
    return "Success"
}
...
val result = getMessage(99)

 

5) break와 continue

break와 continue의 사용

- for이나 while, do...while문 루프를 빠져 나옴

 

▶ break와 continue의 예

package chap04.section3

fun main() {
	for(i in 1..5) {
    	if (i==3) break
        print(i)
    }
    println() // 개행 문자
    println("outside")
}

 

6) break와 라벨

▶ 라벨 없는 break와 라벨을 사용한 break

fun labelBreak() {
    println("labelBreak")
    for(i in 1..5) {
        second@ for (j in 1..5) {
            if (j == 3) break
            println("i:$i, j:$j")
        }
        println("after for j")
    }
    println("after for i")
} 
fun labelBreak() {
    println("labelBreak")
    first@ for(i in 1..5) {
        second@ for (j in 1..5) {
            if (j == 3) break@first
            println("i:$i, j:$j")
        }
        println("after for j")
    }
    println("after for i")
} 

- continue를 사용할 때도 생각해보자

 

🤔 생각해보세요.

 마지막 예제에 있었던 break 비교 예제에 continue를 사용했을 때는 어떻게 되는지 모두 함께 생각해 봅시다.

Q. break 비교 예제에 continue를 사용했을 때

A. 

- 라벨 없는 continue

package chap05.section3

fun main() {
    labelContinue()
}

fun labelContinue() {
    println("labelContinue")
    for(i in 1..5) {
        second@ for (j in 1..5) {
            if (j == 3) continue
            println("i:$i, j:$j")
        }
        println("after for j")
    }
    println("after for i")
}

 

라벨 없는 continue 코드 출력 결과

- 라벨 있는 continue

package chap05.section3

fun main() {
    labelContinue()
}

fun labelContinue() {
    println("labelContinue")
    first@ for(i in 1..5) {
        second@ for (j in 1..5) {
            if (j == 3) continue@first
            println("i:$i, j:$j")
        }
        println("after for j")
    }
    println("after for i")
} 

 

라벨 있는 continue 코드 출력 결과

 

4. 예외 처리

1) 예외(exception)

▶ 실행 도중의 잠재적인 오류까지 검사할 수 없기 때문에 정상적으로 실행이 되다가 비정상적으로 프로그램이 종료되는 경우

- 운영체제의 문제 (잘못된 시스템 호출의 문제)

- 입력값의 문제 (존재하지 않는 파일 혹은, 숫자 입력란에 문자 입력 등)

- 받아들일 수 없는 연산 (0으로 나누기 등)

- 메모리의 할당 실패 및 부족

- 컴퓨터 기계 자체의 문제 (전원 문제, 망가진 기억 장치 등)

 

2) 예외 구문

▶ 예외를 대비하기 위한 구문

try {
    예외 발생 가능성 있는 문장
} catch (e: 예외처리 클래스명) {
    예외를 처리하기 위한 문장
} finally {
   반드시 실행되어야 하는 문장
}

- 반드시 실행해야할 작업이 없다면 finally 블록은 생략하고 try~catch 블록만으로 코드를 구성할 수 있음

 

▶ 실습: 예외 구문 작성하기

package chap05.section4

fun main() {
    val a = 6
    val b = 0
    val c: Int

    try {
        c = a / b
        println("After")
    } catch (e: Exception) {
        println("Exception")
    } finally {
        println("Finally")
    }
}

 

3) 특정 예외 처리

▶ 산술 연산에 대한 예외를 따로 특정해서 잡을 때

''
} catch (e: ArithmeticException) {
	println("Exception is handled. $(e.message)")
}

- 스택의 추적

...
} catch (e : Exception){
    e.printStackTrace()
}
...

 

4) 예외 발생시키기

▶특정 조건에 따른 예외 발생

throw Exception(message: String)

 

▶ throw를 사용한 예외 발생 예제

fun main() {
	
    var amount = 600
    
    try [
    	amount -= 100
        checkAmount(amount)
     } catch (e: Exception) {
     	println(e.message)
     }
     println("amount: $amount")
}

fun checkAmount(amount: Int) {
	if (amount < 1000)
    	throw Exception("잔고가 $amount 으로 1000 이하입니다.")
}

 

🤔 생각해보세요.

 프로그램은 정해진대로만 흘러간다면 아무 문제가 없겠죠. 하지만 다양한 예외 상황을 만날 수 있습니다. 이러한 예외 상황을 생각해보고 어떤것들이 있는지 댓글로 남겨 봅시다. 또한 어떻게 처리할 수 있을까요?

Q. 프로그램의 예외 상황과 처리 방법

A. 제 경험에 빗대어 웹크롤링 프로그램을 생각해본다면, 크게 1) 웹 사이트 주소가 존재하지 않거나  2) Null 데이터를 가져오는 등의  예외 상황이 발생할 수 있습니다.

해당 경우 모두 try-catch문을 사용하여 

1)의 경우, 존재하지 않은 웹 사이트 주소를 알리고 다른 웹 사이트 주소가 남았다면 마저 실행되도록 하였습니다.

2)의 경우, Null 데이터를 가져온 웹 사이트 주소를 알리고 다른 웹 사이트 주소가 남았다면 마저 실행되도록 하였습니다.

 


서포터즈 네임택


감사합니다!