본문 바로가기
공부/kotlin

코틀린: 코틀린 함수

by 샤샤샤샤 2023. 3. 20.

코틀린의 함수

코틀린은 함수형언어와 객체지향언어를 합쳐놓은 언어이기에, 함수형 언어의 특징을 가진다. 

 

형식

fun 함수명( 매개변수: 타입 ) : 반환값 타입 {

    return 반환값

}

한줄함수

함수의 본체 코드가 한줄일때는 중괄호와 return 문자를 생략 가능하다.

fun 함수명 (매개변수 : 타입) : 반환값 타입 = 반환값

반환형 타입 생략(타입 추론)

fun 함수명 (매개변수 : 타입) = 반환값

//ex10
fun main() {
    //함수의 간략한 표현
    println( add(10, 20 ) )
    println( add2(10, 20 ) )
    println( add3(10, 20 ) )
}
fun add(a: Int, b: Int): Int {
    return a + b
}
//함수의 본체코드가 한줄 생략가능하다.
//중괄호와 return문이 생략가능
fun add2(a: Int, b: Int): Int = a + b
//반환형도 생략가능(타입추론)
fun add3(a: Int, b: Int) = a + b

 

함수 매개변수의 기본값

JS처럼 매개변수의 기본값을 설정해줄수 있으며, 이경우 매개변수가 입력되지 않을시 기본값이 자동으로 들어간다.

형식

fun 함수명 (매개변수 : 타입 = 기본값){

     실행코드

}

 

일급객체(First-class Object)

일급객체란 다른 객체들에 일반적으로 적용 가능한 연사을 모두 지원하는 객체를 의미한다. 쉽게 말하자면 조건 1. 변수에 할당할수 있으며, 조건 2. 함수를 인자로 받을수 있고, 조건 3. 함수의 리턴값이 될수 있다.

이렇게 일급 객체가 존재하면 고차함수와 콜백함수를 사용할수 있다.

 

고차함수(High-order function)

일급 객체가 존재할때, 일급객체(함수)를 인자로 받는 함수, 또는 일급객체(함수)를 리턴하는 함수를 말한다.

// func는 익명함수

// 다른 함수를 인자로 받는 경우
function sum( a:Int, b:Int ) { return a + b }

function mulNum(sum, num1, num2) {
  return sum(num1, num2);
}

// 람다를 이용한 고차함수
var ret = highFunc( { x, y -> x + y }, 10, 20 )

콜백함수(Callback function)

전달인자로 받은 함수가 된다.

const sumFunc = function(sum, num1, num2) { return func(num) }
// sumFunc 는 sum함수를 인자로 받고 함수(func)를 리턴하는 고차함수
// sum은 인자가 되는 콜백함수

고차함수 활용

먼저 고차함수를 하나 만들어두자.

fun highOrder(sum: (Int, Int) -> Int, a:Int, b:Int ): Int {
    return sum(a, b)
}
// Int 타입의 매개변수를 2개를 받고 Int타입의 리턴값을 가지는
// 함수 sum과, a, b라는 숫자를 매개변수를 받아 sum(a,b)를 반환하는
// 매개변수로 받는 함수 highOrder

이 함수의 매개변수 sum을 람다로 받는 고차함수를 만들어보자.

    var ret = highOrder( { x, y -> x + y }, 10, 20)
    // ret은 일급객체, highOrder은 고차함수          결과:30

람다식을 변수에 할당할수도 있다.

    //람다식을 변수에 할당하기
    val multi = { x: Int, y: Int -> x * y }
    var ret3 = multi( 10, 20 )

 

반환값이 없는 함수

코틀린에서 리턴값이 없는 함수는 Unit타입을 반환하며, 이는 자바의 void와 동일한 역할을 한다고 이해하면 된다.

 

람다식 표현

람다식은 간단하게 함수를 표현하기 위해 고안된 방식이다. 람다식 내부에서는 return을 사용할수 없으며, 반환값은 저절로 추정된다.

var lamda : () -> Unit= { println("람다") }
var lamde2 = lamda
lamda2()                      // 결과: 람다

일반함수를 람다식으로 변환해서 사용가능하다. 이때, 타입은 일치해야 한다.

:: 연산자

fun funcParam(a:Int, b:Int, c: (Int, Int) -> Int): Int{
    return c(a, b)
}
// 고차함수
fun div(a:Int, b:Int):Int = a / b
// 매개변수 a와 b를 나눈값을 반환하는 일반함수

 

본래 고차함수 funcParam을 어떤 변수에 할당하기 위해선 함수 c를 람다로 표현해야 하지만, 이미 만들어져있는 div를 ::연산자를 통해 전달인자로 넣어줄수 있다.

    val ret = funcParam(10, 2, { x, y -> x / y} )
// 일반적인 표현식
    val ret2 = funcParam(10, 2, ::div)
// 일반함수를 람다로 바꿔서 표현하는 방식

 

익명함수

이름없는 함수를 의미한다.  변수에 담아서 사용한다.

예)

    val sum2 = fun(x: Int, y: Int): Int = x + y
    println( sum2(10,20) )   // 결과 : 30

 

인라인함수 (Inline function)

일반적으로 외부의 함수를 불러와 사용하면 프로그램은 돌아가던 작업을 잠시 멈추고 스택에 작업 내용을 저장한 뒤, 분기를 나눠 주소값을 참조해 함수를 실행하고, 실행이 끝나면 저장된 작업내용을 복구한뒤 결과값을 대입하는 방식이다. 반면 인라인 함수는 컴파일 단계에서 함수 내부 코드를 복사해 붙여넣기 때문에, 스택에 저장하거나 분기를 나누는 작업이 사라진다.

함수호출 오버헤드가 사라지며 컴파일 단계에서 컴파일러가 자의적으로 최적화를 할수 있다는 장점이 있으나, 반대로 코드 자체가 길어지고 중복이 생길수 있다는 단점이 존재하기에 짧은 줄의 함수만 인라인으로 사용한다.

//인라인함수
inline fun inlineSum(a:Int, b:Int): Int {
    return a + b
}

fun main() {
    inlineSum(10, 20)
}

함수 앞에 inline을 적어주면 인라인 함수로 인식된다.

 

확장함수

이미 만들어져있는 클래스나 언어가 자체적으로 지원하는 클래스(타입)에 기능을 덧붙이는 것을 의미한다. 이 기능을 사용하면 지원하지 않는 함수도 내장함수처럼 사용 가능하다.

// String 함수에 더 긴 문자열을 반환하는 getLongString이라는 기능을 추가
fun String.getLongString(param: String): String =
    if( this.length > param.length ) this else param
    
fun main{
```kotlin
//ex15
fun main() {
    val source = "Hello World!"
    val target = "Hello"
    println( source.getLongString(target) )
}
// 결과 : Hello World!

 

중위함수( Infix Function )

클래스의 맴버를 호출할때 점( . )을 생략하고 함수 이름뒤에 소괄호를 사용하지 않는 방식으로 사용하는 함수.

함수가 마치 연산기호처럼 보이기 때문에 더 직관적으로 표현할수 있다.

함수 앞에 infix를 적으면 된다.

// Int클래스에 대한 확장함수를 중위함수로 선언했다.
infix fun Int.multiply(x: Int): Int {
    return this * x
}

fun main() {
    //일반표현법
    val ret1 = 3.multiply(10)
    println( ret2 )   // 결과 : 30
    //중위함수 표현법
    val ret2 = 3 multiply 10
    println( ret2 )   // 결과 : 30
}

 

 

범위 연산자 ..

코틀린은 숫자의 범위를 나타낼때 .. 을 사용할수 있다. 원한다면 증감 크기도 조절 가능하다.

형식

(시작 숫자) .. (끝 숫자) step (증감 크기)

fun main() { 
 var total = 0
    for( num in 1..100 step 2 ) //한줄짜리 반복문 중괄호 생략
        total += num
    println( "홀수값의 합 : $total" )

    var num = 5
    if( num in 1..10 ) {
        println( "num은 1부터 10사이에 있음" )
    }
}

 

switch - case문 대신 when in문

기존의 switch case문을 대체하는 코틀린의 문법이다. 만약 when문의 매개변수가 존재한다면 in을 통해 .. 연산자를 사용한 조건문을 표현할수 있으며, 없더라도 직접 조건문을 써줄수 있다.

조건문이 짧고 그에 따른 실행문 또한 간단할때 나타내기 좋다.

//ex18
 fun main() {
 // reaLine(): 사용자가 직접 콘솔에 입력한 값을 받음
 //            자바의 Scanner의 역할과 동일
     val score = readLine()!!.toDouble()
     var grade: Char = 'F'

// 인자를 받는 when문
 when (score) {
        in 90.0 .. 100.0 -> grade = 'A'
        in 80.0 .. 89.9 -> grade = 'B'
        in 70.0 .. 79.9 -> grade = 'C'
        !in 70.0 .. 100.0 -> grade = 'F'
    }
    println( "Score: $score, Grade: $grade")

//인자를 받지 않는 when문
when {
         score >= 90.0 -> grade = 'A'
         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")
 }

readLine() : 사용자가 직접 콘솔에 입력한 값을 받음

 

숫자뿐만이 아니라 객체의 타입이나 문자열 역시 when문을 통해 조건 비교 가능하다.

fun main() {
    justPrint(1)
    justPrint("Hello")
    justPrint(1)
    justPrint(12345L) //Long
    justPrint(MyClass()) //Not a String
}
class MyClass{ }
fun justPrint(obj: Any) {
    when(obj){
        1 -> println("Int타입: $obj")
        "Hello" -> println("String타입: $obj")
        is Long -> println("Long타입: $obj")
        !is String -> println("String타입이 아님")
        else -> println("Unknown")
    }
}

 

'공부 > kotlin' 카테고리의 다른 글

코틀린의 클래스  (0) 2023.03.21
코틀린: 코틀린의 기본 문법  (0) 2023.03.19