F.R.I.D.A.Y.

Kotlin: 07 함수 본문

DEV/Kotlin

Kotlin: 07 함수

F.R.I.D.A.Y. 2020. 3. 31. 22:36
반응형

 이번 시간에는 함수에 대해 알아봅니다. 함수를 사용하는 순간부터 프로그램 작성 방식에 대한 패러다임이 바뀔 겁니다. 꼭 알고 넘어가길 바랍니다.

 


함수

 우리는 살면서 많은 일을 합니다. 밥을 먹을 수도 있고, 일을 할 수도 있으며, 지금처럼 공부를 할 수도 있죠. 함수란 이처럼 특정 작업을 하는 일련의 동작을 한데 모아놓은 집합으로 생각할 수 있습니다. "밥을 먹다"로 예를 들어보겠습니다. 우리는 다른 사람들에게 "밥을 먹는다"라고 하지, 밥을 먹을 때 어떤 행위를 하는지 풀어서 설명하지는 않습니다.

밥을 먹는다 수저를 든다
수저로 밥을 푼다
입에 수저로 푼 밥을 넣는다
수저를 놓는다
젓가락을 집는다
반찬을 정한다
정한 반찬을 젓가락으로 집는다
젓가락으로 집은 반찬을 입에 넣는다
위 행동을 밥을 다 먹거나 배가 부를 때까지 반복한다

 뭐 이렇게 풀어서 설명할 수 있겠죠. 프로그래밍으로 한다면 표의 오른쪽처럼 풀어쓰는 방식이 main 함수에 모두 작성하는 행위라 한다면, 왼쪽의 "밥을 먹는다"라는 문장은 main 함수에 코드를 모두 작성하지 않고 부분 부분 자주 사용하는 코드를 새로운 함수에 만든 것이라 볼 수 있습니다.

 함수라는 개념이 이해가 되셨나요?

선언

 코틀린의 함수의 선언은 아래와 같습니다. 이미 main 함수를 여러 번에 걸쳐 작성해보셨으니 기본 틀은 잘 알고 있으리라 생각합니다.

fun functionName(){
	// code...
}

 기본형은 위와 같지만, 실제 일반 표현식은 아래와 같습니다.

fun functionName([arg: type, ...]) [: type]{
    // code...
    [return value]
}

 대괄호([])로 묶여있는 곳은 필요에 따라 작성하는 곳입니다.

 전체 구조를 설명하면 아래와 같습니다.

fun

 앞으로 함수를 선언하겠다는 도입부와 같습니다.

functionName

 함수를 호출할 때 작성할 함수의 이름입니다. 함수 이름 규칙은 알파벳, 숫자, 언더스코어

[arg: type, ...]

매개 변수를 작성하는 곳입니다.

더보기

# 매개 변수란?

 만일 main 함수에서 두 값을 더하는 sum이라는 함수를 호출했다면, sum 함수에는 main 함수의 두 값이 전달되어야 합니다. 이때, 값의 전달을 맡아주는 곳이 매개 변수입니다.


 이와 별개로, 매개 변수와 인자, 인수, 파라미터(Parameter), 아규먼트(Argument)를 혼동하는 경우가 많습니다.

fun sum(a:Int, b:Int) sum(3,5)
매개 변수 인수
인자 아규먼트(Argument)
파라미터(Parameter)  

 함수 선언부의 괄호 안에 들어가는 것이 매개 변수, 인자, 파라미터입니다.

 함수 호출에서 괄호 안에 들어가는 값이 인수, 아규먼트입니다.

[: type]

 함수의 연산 결과로 반환할 데이터의 자료형입니다. 흔히 반환 자료형으로 부릅니다. 많은 함수들이 반환 결과를 가지고 있지만, 어떤 경우에는 반환할 값이 없어 굳이 작성하지 않아도 되는 경우가 있습니다. 이 경우, 아래와 같이 그냥 작성해도 괜찮습니다.

fun sumPrint(a:Int, b:Int)/* 반환 자료형이 없음*/{
    println(a+b)
}

 반환 자료형이 존재하지 않으므로 return 구문을 작성하지 않아도 됩니다.

더보기

# 코틀린의 반환 자료형에 대한 고찰

 반환 자료형에 대해서는 주의할 점이 한 가지 있습니다. 다른 언어, 예컨대 C 언어에서는 반환 자료형이 없을 때, void라는 자료형을 사용함으로써 반환할 자료형이 없다는 것을 알려줍니다. 그러나 코틀린은 다릅니다. 코틀린은 반환할 값이 존재하지 않아 반환 자료형을 공백으로 남겨두게 되면 컴파일러가 이를 인지해 Unit라는 자료형을 추가합니다. 위 sumPrint 함수를 예제로 하면 실제 컴파일 시 코드는 아래의 코드를 컴파일한다고 보면 됩니다.

fun sumPrint(a:Int, b:Int): Unit{
    println(a+b)
}

 자세한 내용은 추후에 공부를 하며 함께 알아보겠습니다.

[return value]

 반환 자료형이 있을 때 사용하는 부분입니다. 함수의 연산을 통해 얻은 값을 해당 함수를 호출한 부분을 되돌려 주는 역할을 여기서 담당합니다. return 키워드는 함수의 즉각적인 종료와 함께 뒤따라오는 값을 caller[# 해당 함수를 호출한 곳]로 되돌려주기에 return 키워드를 사용한 이후의 코드는 실행되지 않습니다.[# 물론 이것을 의도적으로 이용하는 경우도 있지요.]

fun Test():Int{
    val value:Int = 5
    
    return value
    
    println(value) // 앞선 return 키워드에 의해 실행되지 않음.
    
}

Test 함수의 value(5)를 출력하는 println 함수가 실행되지 않음

사용

 예시로 두 수를 받아서 더한 값을 반환하는 함수를 만들어 보겠습니다. 두 수의 값과 반환 자료형은 Int로 고정합니다.

fun sum(a:Int, b:Int) : Int{
    return a + b
}

 기본형의 사용은 위와 같습니다.

더보기

# 위 코드를 더 줄이고 싶어요.

 위의 sum 함수는 조금 더 줄일 여지가 있습니다. sum 함수와 같이 매개 변수로 들어온 값으로 연산해 바로 반환되는 함수의 경우, 중괄호와 return 키워드를 제거한 대신 등호를 사용하는 아래와 같이 작성할 수 있습니다.

fun sum(a:Int, b:Int) : Int = a + b

 만일 반환할 데이터의 자료형이 매개 변수의 자료형과 같다면 반환 자료형 또한 제거할 수 있습니다.

fun sum(a:Int, b:Int) = a + b

매개 변수에 값 넣기

 매개 변수에 대한 이야기는 몇 가지 할 게 있어서 큰 단락으로 따로 빼왔습니다. 매개 변수를 한 번 선언하면 함수를 사용할 때 꼭 집어넣어야 합니다. 그렇지 않으면 함수를 구성하고 그 함수가 작업할 때 필요한 정보가 부족하니 오류를 출력하는 것은 자명한 일입니다. 대개 이런 경우에는 함수에 값을 넣어주어야 하지만, 가끔씩만 변경해야 하는 정보들이 있습니다.

매개 변수 기본값

 위 설명처럼 만일 필요한 매개 변수이긴 하지만, 늘 같다가 가끔씩만 변경되는 값이 존재할 수 있습니다. 이 경우 매개 변수의 기본값을 활용해 코드 작성을 더 용이하게 할 수 있습니다. 사람의 이름을 출력하는 함수가 존재한다고 봅니다.

fun printName(name:String, annoy/*익명*/:Boolean){
    if(annoy == false) println(name)
    else println("Unknown")
}
더보기

# 코드 설명

 매개 변수 name은 사람의 이름을 저장하고 있는 String 타입의 변수입니다. annoy는 사람의 이름을 출력할 때 실명을 출력할 것인지 익명으로 실명을 감출 것인지를 결정하는 Boolean 타입의 변수입니다.

 중요한 것은 annoy로서, annoy가 false일 경우

if(annoy == false)

 구문에서 참이 되어 실명이 출력되고, annoy가 true일 경우 위 구문에서 참이 성립되지 않아 else 구문이 실행됩니다. 지금은 코드를 보고 몰라도 괜찮습니다. 아직 if문을 배우지 않았으니까요. 나중에 if문을 배우게 된다면 쉬운 코드가 될 것입니다.

 사람들의 이름을 매번 익명으로 출력할 필요는 없을 겁니다. 대부분 실명을 쓰다가 가끔씩만 익명을 이용하겠죠. 그렇다면 매개 변수 annoy는 기본값으로 false를 가지면 좋겠군요. 그럼 익명이 필요할 때만 annoy의 값을 true로 만들어주면 될 테니까요. 이 경우 우리는 annoy에 기본값을 넣어줄 수 있습니다.

fun printName(name:String, annoy:Boolean = false){
    if(annoy == false) println(name)
    else println("Unknown")
}

 변수를 선언하는 것과 마찬가지로 매개 변수와 그 자료형을 작성하고 나서 값을 대입하듯이 등호를 입력하고 값을 넣어주면 해당 값이 annoy의 기본값이 됩니다.

 기본값이 설정되면 아래와 같이 annoy는 변경이 필요할 때만 작성할 수 있습니다.

fun main(){
    printName("홍길동")
    printName("심청이", true) // 익명
    printName("박첨지")
    printName("흥부", true)   // 익명
    printName("오성")
    
}

두 번째 annoy 변수를 true로 준 함수만 Unknown으로 표시되는 모습

이름과 함께 호출하기

 프로그램의 규모가 커지다 보면 각 매개 변수가 어떤 의미인지 알기 위해 변수 이름 자체에 그 의미를 부여하는 경우가 많습니다. 그럼에도 불구하고 매개 변수 작성에 어려움을 겪을 수 있습니다. 어떤 매개변수가 몇 번째에 위치하는지 헷갈릴 수 있기 때문입니다. 그래서 코틀린에서는 매개 변수의 이름을 함께 작성해 순서에 상관없이 입력할 수 있도록 기능을 제공합니다.

fun printPersonData(name:String, age:Short, tall:Float){
    println("이름: $name\n나이:$age\n키:$tall")
}

 사람의 이름, 나이, 키를 출력하는 함수 printPersonData가 있다고 합니다. 이 경우 대부분 매개 변수 선언 순서에 따라 이름, 나이, 키의 순으로 데이터를 작성할 것입니다. 그러나 매개 변수의 이름을 함께 작성하면 아래처럼 순서를 무시할 수 있습니다.

printPersonData("홍길동", 16, 170.1f)

printPersonData(age=16, name="홍길동", tall=170.1f)

매개 변수의 이름과 함께 작성하면 선언 순서를 무시하는 모습


가변 인자 사용하기

 함수는 대개 인자의 수가 정해져 있습니다. 그러나 종류에 따라서는 들어오는 인자의 수를 알 수 없을 때가 있습니다. C언어를 예로 들어볼까요? 가장 기본적인 입출력 함수인 printf나 scanf 함수의 경우에는 들어오는 인자의 수가 몇 개인지 알 수 없습니다.[# 물론 첫 인자로 들어오는 문자열에 인자의 수가 몇 개인지 추측할 수 있는 정보를 가지고 있지만, 이점은 넘어가도록 합니다.] 그래서 우리는 가변 인자라 불리는 기술을 사용할 수 있습니다.

가변 인자

 가변 인자(Variable Argument)란, 말 그대로

네이버 사전

 

 달라질 수 있는 인자를 말합니다. 이때 달라진다는 것은 인자의 수를 말합니다. 가변 인자는 이렇게 사용할 수 있습니다.

선언

매개 변수를 선언할 때, vararg 키워드를 입력하면 해당 매개 변수가 가변 인자를 담을 수 있는 변수가 됩니다.

fun foo(bar:Int){
   println("$bar ")
}

 하나의 값만 전달받을 수 있던 bar 변수는 vararg 키워드를 넣음으로써

fun foo(vararg bar:Int){
   println("$bar ")
}

vararg 키워드를 입력하지 않았을 때, 컴파일 오류가 발생한다.
vararg 키워드를 입력했을 때, 이상한 출력은 나왔지만 컴파일 오류는 발생하지 않는다

 vararg를 이용해 선언한 가변 인자 bar 변수는 이제 마음대로 입력할 수 있습니다. 물론 이렇게 가변 인자로 만든 변수의 데이터를 읽기 위해서는 단순히 변수 이름을 작성하는 것으로는 끝나지 않습니다. 그때는 반복문과 in 키워드를 이용해 읽을 수 있습니다.

fun foo(vararg bar:Int){
   for(value in bar){
       println(value)
   }
}

 자세한 내용은 앞으로 배울 반복문 부분에서 다루도록 합니다.


Next.

 

Kotlin: 08 분기 제어-if문

이번 시간에는 입력에 따라 프로그램이 하는 작업을 선택(분기)할 수 있는 if문 대해 알아봅니다. 이 조건문을 배움으로써 이젠 조금 더 나은, 간단한 게임[# 이라 말하고 싶지만, 결국엔 퀴즈 정도에 가깝겠네요..

pang2h.tistory.com

# index

728x90
반응형

'DEV > Kotlin' 카테고리의 다른 글

Kotlin: 09 분기 제어-when  (0) 2020.04.02
Kotlin: 08 분기 제어-if  (0) 2020.04.01
Kotlin: 06 세이프콜과 non-null 단정  (0) 2020.03.29
Kotlin: 05 연산자  (0) 2020.03.28
Kotlin: 04 자료의 비교와 변환  (0) 2020.03.27
Comments