본문 바로가기

프로그래밍 언어/코틀린_Kotlin_더파기

Kotlin더파기_03_Function_함수

function (함수)

 

함수의 개념은 기초01에서 자판기에 비유했다.

함수는 특정 기능을 가진 부품과 같다.

이 부품들을 결합하여 다양한 형태의 프로그램을 만들 수 있다.

필요하면 직접 코딩하여 만들 수 있다.

그러므로 코틀린을 배우는 핵심 중의 하나가 함수를 만드는 방법을 배우는 것이다.

 

앞에서 많이 사용했던 println( )도 '화면에 데이터를 출력'하는 기능을 가진 함수다.

이 함수는 코틀린 표준 라이브러리에 포함되어 있어서 언제든지 필요할 때 불러와서 사용할 수 있다.

 

인텔리제이를 실행하고 앞 글에서 만들었던 프로젝트 NetHack를 불러온다.

아래쪽에는 변수 characterCondition을 만들고 조건에 따라 문자열을 출력하는 코드를 만들었다.

이 부분을 함수로 만들어보자.

함수를 만들기 위해 우리가 직접 코드를 함수에 맞게 추가하고 작성할 필요없다.

똑똑한 인텔리제이가 함수로 만들어 주는 기능이 있다.

이렇게 코드 일부를 함수로 만드는 것을 refactoring(리팩토링)이라고 한다.

re는 again(다시) 라는 뜻이고, fact는 make(만들다)의 뜻이므로 refactor은 '다시 만들기'라는 의미이다.

factory는 '만드는 곳' 즉, 공장이다.

 

함수로 변환하려면 함수로 바꾸고자 하는 부분을 선택한다.

v부터 마지막 } 닫는 중괄호까지 마우스로 끌어서 선택한다.

 

 

마우스 포인터를 선택한 파란색 위에 두고 마우스 오른쪽 버튼 (Mac은 Control+클릭) 클릭하고 Refactor =>Function을 선택한다.

 

위와 같은 대화상자가 나타난다.

Visibility(가시성), Name(이름) , Parameters(매개변수, 파라미터), Signature Preview(특징 미리보기)의 옵션이 있다.

Visibility(보임, 가시성)은 패키지 내부에서만 접근할 수 있는 private을 선택한다.

Name에는 함수 이름을 적어주어야 한다.

formatCharacterCondition이라고 입력한 후 OK버튼을 누른다.

 

 

선택했던 부분이 노란색 중괄호 안으로 들어가고 fun함수로 만들어져서 main( )함수 바깥으로 이동했다.

그리고 main() 내부에 있던 원래 자리에 새로운 녹색 코드가 만들어졌다.

 

만들어진 함수 안에 characterCondition에 노란 물결선에 대해서는 조금있다가 알아보기로 하고 함수가 어떤 모양인지 부터 알아본다.

 

함수의 구조

 

함수는 크게 두 부분으로 나눌 수 있다.

 

header 

body

 

header(헤더)는 머리에 해당하고 body(바디)는 중괄호{}로 둘러쌓인 몸체에 해당한다.

 

함수의 구조에 대해 기초에서 부분적으로 배운것을 종합해서 알아보자.

 

Header

 

함수의 header는 5개 부분으로 나누어진다.

 

 

왼쪽부터 '가시성 제한자' , '함수 선언 키워드' , '함수 이름' , '매개변수(파라미터)' , '반환 타입'

 

1. 가시성 제한자(visibility modifier ;보기 제한자) : 다른 함수에게 보여줄 지 말지를 결정함, 생략 가능

생략하면 보기 제한자를 '접근 제한 없음'인 public 으로 간주한다. 

private은 '개인 전용'의 뜻으로 내부에서만 접근 가능하다. 즉, Game.kt 파일에서만 이 함수를 사용할 수 있다.

 

2. 함수 선언 키워드: 함수(function)의 앞부분 세글자 fun을 함수 선언 키워드로 입력함.

 

3. 함수 이름: 함수명은 첫글자를 소문자로, 연결 단어의 첫글자는 대문자로 하는 카멜표기법(Camel)으로 작성한다.

 

4. 매개변수(Parameter): 함수의 매개변수에는 함수 실행에 필요한 받을 데이터의 변수명과 타입을 적는다. 없는 경우도 있다. 함수의 매개변수는 함수의 내부에서 값을 변경할 수 없으므로 읽기전용 변수인 val 이다.

 

5. 반환 타입(Type) : 함수는 입력값으로 내부 기능을 사용해서 어떤 결과값을 만들어 내는 것이 주 기능이다. 그러므로 대부분의 함수는 결과를 만들어낸다. 그 결과값의 타입(Type)을 반환 타입(return type)이라고 한다. 여기서는 반환타입이 String(문자열)이다.

 

body

 

 

함수의 바디(본체)는 header 다음에 중괄호{ }를 사용하여 표현한다.

여기에는 결과값을 되돌려 주는 return 키워드로 시작하는 반환문을 만들 수 있다.

위 코드에서 리턴문은 결과값을 반환하므로 함수의 본체 마지막(when문 바깥)에 정의되어 있다.

 

return characterCondition은 결국 when 표현식으로 작업한 결과값을 변수 characterCondition에 저장되면 그 변수값을 반환한다는 말이다.

 

함수의 범위(scope)

 

위 코드에서 val characterCondition은 함수 안에서만 존재하는 변수이다.

이렇게 어떤 범위(scope)내에서만 한정되어 존재하는 변수를 local variable(로컬 변수, 지역변수)라고 한다.

즉, fun level (함수 수준)에서 노는 변수이다.

 

더파기01에서 상수인 변수 const val MAX_SCORE 를 선언했었다.

 

<더파기01_변상수 타입 참고>

이 상수 변수는 fun함수 수준이 아닌 Hello Kotlin.kt 라는 파일 레벨(file level)에서 노는 변수이다.

노는 레벨이 다르다는 말이다.

기본적으로 파일 레벨의 변수는 프로젝트의 어디에서든 사용할 수 있다. 

 

그리고 변수 characterCondition는 마지막에 결과값을 return으로 반환해 버리면 다른 역할이 없다.

그러므로 위 코드에서  characterCondition이라는 변수가 중복되고 있는 것이 느껴지는가?

이 문제는 마지막에 처리하기로 한다.

 

함수의 호출

 

함수가 만들어졌고 가동해서 결과값도 만들어 낼 수 있는 기능이 완성되었다.

그러면 사용할 곳이 있어야 한다.

인텔리제이는 코드를 함수로 변환한 후 원래 있던 자리에 함수를 사용할 수 있는 변수를 만들어 준다.

 

이렇게 함수를 사용할 수 있게 불러오는 것을 call (호출)이라고 한다.

함수를 호출할 때는 위에서 보듯이 함수명(매개변수) 형태로 적어주면 된다.

 

함수로 변환 후 실행해 보면 변환 전과 같은 결과가 나온다.

하지만 함수로 만들었을 때와 만들지 않았을 때의 차이는 명확하다.

 

이제 나머지 코드에서 함수로 refactor할 수 있는 곳을 찾아보자.

 

 

OK버튼을 누르면 다음과 같이 함수가 만들어진다.

 

 

위 코드에서 정리할 부분은 println()함수 2개가 남았다.

main()함수에서 출력되는 부분은 println() 이므로 2개를 합쳐서 표현해 보자.

 

위쪽 println함수 부분을 아래로 옮겨 보자.

 

위 println 함수 2개를 합하면 다음과 같이 나타낼 수 있다.

아래 println함수 1개로 수정한 후 위의 2개는 지운다.

 

 

최종적인 모습은 다음과 같다.

 

 

마지막으로 characterCondition 변수에 노란색 밑줄을 해결해보자.

새로 만든 함수인 sizeStatus에도 노란색 밑줄이 생겼다.

마우스 포인터를 변수 위로 가져가면 안내 메시지가 나타난다.

 

 

내용은 다음과 같다.

"이 변수는 아래 return에서만 사용되므로 inlined 되는 것이 좋다."

변수가 중복되고 결과값만 반환하면 되므로 합치는 것이 좋다는 말인것 같다.

파란색 Inline variable(인라인 변수)를 눌러서 변환하자.

 

변수가 return으로 바뀌었다.

 

최종 코드를 보자.

 

 

코드가 깔끔하고 멋지게 변한것 같지 않은가?

실행해 보면 다음과 같이 결과가 나타난다.

 

 

결국 지금까지의 작업이 <게임의 주인공 이름: 현재 상태 , 크기 상태> 결과를 출력하기 위한 작업이었다.

모든 변수와 함수는 저 결과를 만들어 내기 위한 도우미들 이었다.

약간 허무하지만 함수를 만드는 법과 그 유용함을 알게된 경험이었다.

 

함수의 매개변수(parameter)

 

함수를 하나 더 만들어 보자.

주인공이 stone(돌)을 던지는 함수이다.

 

함수명은 castStone이고 매개변수가 없기 때문에 괄호만 표시한다.

이 함수가 실행하는 내용은 '돌을 던진다'를 출력하는 함수이다.

그리고 main함수에서 호출하려면 main()함수 실행 부분 마지막에 castStone() 함수를 적는다.

실행해보자.

 

이번에는 돌을 던지는 개수를 바꾸기 위해 매개변수를 추가해 보자.

매개변수명은 stoneNum라고 하자.

 

 

만약 돌 던지는 개수(2개)를 기본값으로 고정하려면 아래와 같이 매개변수에 값을 정하면 된다.

 

 

Single Expression(단일 표현, 싱글 표현)

 

castStone함수 처럼 하나의 명령만 실행하는 경우 함수를 더 간단하게 표현하는 것을 single expression이라고 한다.

castStone함수를 단일 표현으로 바꾸면 다음과 같다.

 

중괄호를 없애고 대입연산자(=)를 연결한다.

또한 header(헤더)의 반환타입, body(실행부분)의 return도 모두 생략할 수 있다.

 

앞에서 만들었던 formatCharacterCondition()함수도 단일표현으로 바꿀 수 있다.

when 조건문에서 나온 결과를 변수 life에 저장해서 반환하는 하나의 실행만 있기 때문이다.

 

 

이렇게 단일(싱글)표현으로 나타낼 수 있는 조건은 실행이 하나(싱글)일 때만 가능하다는 것을 기억하자.

 

Unit (유닛) 함수

 

castStone()함수를 보면 반환타입과 return 구문이 없는 함수이다.

이런 함수를 코틀린에서는 반환타입이 Unit인 Unit함수라고 한다.

반환타입이 무엇인지 알아보려면 함수명에서 Ctrl+Shift+P 를 눌러보면 알 수 있다.

 

Unit은 하나 또는 단위라는 뜻이며 여기서는 '아무 값도 반환하지 않는 함수'의 타입이라는 뜻으로 사용한다.

반환하지 않는 함수란 return 키워드를 사용하지 않은 경우를 말한다.

이렇게 알송달송한 Unit 타입을 만든 이유는 generic(제네릭)을 사용하기 위해서이다.

 

generic은 여러 타입을 가진 함수를 가능하게 하는 중요한 기능이며 반드시 반환타입이 있어야 한다.

또한 아무것도 반환하지 않는 제네릭 함수도 Unit을 사용할 수 있다.

제네릭에 대해서는 다음에 알아보기로 한다.

 

타입에는 Nothing 타입이라는 것도 있다.

이 타입은 Unit처럼 값을 반환하지 않는 함수를 나타내는 것은 같다.

하지만 함수 실행이 끝나면 호출 코드로 제어를 넘기지 않고 끝내는 것이 다르다.

Nothing타입은 예외(exception)적인 상황을 만들 때 사용할 수 있다.

 

대표적인 내장 함수가 TODO함수 이다.

TODO함수는 해야할 일이 있다는 안내문을 보여주는 함수이다.

함수를 만들고 실행부분을 아직 만들지 않았을 때 이 함수를 표시한다.

이 함수는 예외를 만들어 컴파일러가 에러로 처리하지 않도록 한다.

 

named function argument(이름있는 함수 인자)

 

함수를 호출할 때 매개변수에 인자(argument)가 전달된다.

castStone(2)

 

그런데 인자가 여러 종류일 수도 있다.

castStone ( 2, 3 )

이 경우에는 함수를 호출할 때 매개변수의 순서대로 인자를 적어야 한다.

 

이런 경우 인자를 적는 다른 방법이 있다.

매개변수명과 함께 적는 것이다.

 

castStone ( stoneNum = 2, size = 3 )

 

이것이 이름있는 함수 인자다.

이것의 장점은 첫째 인자가 많을 경우 어떤 인자인지 헷갈리지 않는다는 것이다.

그리고 순서를 바꿔서 인자를 표시할 수 있다는 것이다.

castStone ( size = 3, stoneNum = 2 )

 

function overloading (함수 오버로딩)

 

함수 오버로딩은 함수 이름은 같지만 매개변수나 타입이 다른 함수로 만드는 것을 말한다.

예를 들어 아래에 'castSpear(창 던지기)'함수를 3개 만들고 메인 함수에서 함수 인자를 다르게 해서 호출해 본다.

 

main함수에서 castSpear()함수를 호출하면 함수 이름은 같지만 호출 인자(argument)의 개수, 타입이 맞는 것을 찾아서 실행해 준다.

 

 

Wraven...