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

Kotlin: 18 람다식(Lambda Expression) part2. Advance 본문

DEV/Kotlin

Kotlin: 18 람다식(Lambda Expression) part2. Advance

F.R.I.D.A.Y. 2020. 8. 27. 23:48
반응형

 이전 시간에 람다식의 기초 부분을 배웠다면 이번엔 람다식을 조금 더 심도 있게 다뤄보겠습니다.


람다식의 이용

인자에 사용하기

 인자에 사용하는 방법은 여러 가지가 있습니다. 이번에 소개하는 문법은 인자의 마지막에 들어가는 람다식은 소괄호 밖으로 빼낼 수 있다는 것입니다.

 

 람다식 두 개를 인자로 받는 테스트 함수를 작성해보겠습니다.

fun TestFunc(lamb1:()->Unit, lamb2:()->Unit){
    lamb1()
    lamb2()
}

 이 함수에는 단순 출력을 하는 람다식을 넣을 것입니다. 코드를 단순하게 하기 위해서 받을 인자도, 반환하는 인자도 존재하지 않는 람다식으로요.

fun TestFunc(lamb1:()->Unit, lamb2:()->Unit){
    lamb1()
    lamb2()
}

fun main(args:Array<String>){
    TestFunc({println("lambda Exp 1")}, {println("lambda Exp 2")});
}

 보기에 불편합니다. 그러나 이 기능을 이용하면 아래처럼 코드를 다시 작성할 수 있습니다.

TestFunc({println("lambda Exp 1")}){
    println("lambda Exp 2")
}

 마지막 람다식이 소괄호 밖으로 나왔습니다. 람다식은 이 방법이 가능합니다.

 

 참고할 것은, 마지막 인자가 람다식인 경우에만 마지막 람다식을 밖으로 뺄 수 있다는 것입니다. 람다식이 여러 개일 경우에도 마지막 람다식만을 밖으로 빼낼 수 있으니 꼭 기억해서 혼동하지 않도록합니다.

 

변수에 할당된 람다식은?

 이전 시간에 변수에도 람다식을 할당해 사용해봤습니다. 이번에 소개한 방법이 변수에 할당한 람다식에도 허용될까요?

 

fun TestFunc(lamb1:()->Unit, lamb2:()->Unit){
    lamb1()
    lamb2()
}

fun main(){
    val lamb1:()->Unit ={println("Hello world1")}
    val lamb2:()->Unit = {println("world famous language 2")}

    TestFunc(lamb1, lamb2)
    println("--------------------")
    TestFunc(lamb1)lamb2
    
}

 코드를 위와 같이 구성했습니다. 실제로 가능한지는 직접 해보시는 걸 추천해드립니다. 만일 안된다면 어떤 이유에서 안되는지 고민하는 것도 공부에 큰 도움이 될 것입니다.

더보기

# 고민해보셨나요? 

 먼저 코틀린은 세미콜론을 사용하지 않으면 코드 한 줄에 하나의 명령만 작성할 수 있습니다. 바로 아래 코드에 이를 적용하면 한 줄에 두 명령이 들어가 있는 것으로 볼 수 있습니다.

TestFunc(lamb1)lamb2

 TestFunc(lamb1)이라는 명령과 lamb2라는 명령이 한 줄에 들어가 있는 것이죠. 따라서 코틀린 문법상 올바르지 않기 때문에 변수에 할당한 람다식을 이렇게 작성할 수는 없습니다.

# 이해가 되셨나요?

 

변수에 할당된 람다식 이용하기

 변수에 할당된 람다식은 아래처럼 이용할 수 있습니다.

fun TestFunc(lamb1:()->Unit, lamb2:()->Unit){
    lamb1()
    lamb2()
}

fun main(args:Array<String>){
    val vLamb:()->Unit = {println("Hello world!")}
    
    TestFunc(vLamb){
        vLamb()
    }
}

 이 방식은 람다식 안에 vLamb라는 람다식을 다시 실행시키는 구조를 가지고 있습니다.

 

    TestFunc(vLamb){
        vLamb()
    }

 

 변수에 할당할 때 중괄호로 한 번 더 감싸는 구조의 람다식을 만들 수 있습니다.

{{println("Hello world")}}

 와 같이 말이죠. 주의할 점은 이 람다식의 타입은 ()->Unit이 아닙니다. 람다식이 람다식을 감싸고 있는 구조이기 때문에 실제 람다식을 변수에 할당할 때는 아래와 같이 작성합니다.

val vDblLamb:()->()->Unit = {{println("Hello world!")}}

 또 람다식이 람다식으로 싸여 있는 구조이므로 실행할 때도 주의가 필요합니다.

val vDblLamb:()->()->Unit = {{println("Hello world!")}}

vDblLamb()   // 출력 안됨
vDblLamb()() // Hello world! 출력됨

 코드에서 알 수 있듯이 중복으로 감싸진 람다식은 감싸진 만큼 괄호를 더 써주어야 내부의 코드가 실행됩니다. 하나만 작성한 vDblLamb()의 경우에는 {println("Hello world!")}를 가지고 있는 데이터라고 보면 이해가 쉬울 것 같습니다.

코드 의미
vDblLamb {{println("Hello world!")}}
vDblLamb() {println("Hello world!")}
vDblLamb()() println("Hello world!")

 


매개변수 관리

 함수를 표현할 수 있는 자료형이 있기에 우리는 이미 만들어진 함수를 내 함수에서 불러와 사용할 수도, 매개변수로 넣어서 내 함수를 다른 개발자가 만든 함수에 넣을 수도 있습니다. 그 과정에서 함수를 표현하는 자료형은 동일해야 합니다. 같아야 알맞게 함수를 넣고 관리할 수 있을 테니까요.

 

매개변수의 선택적 사용

 다음 함수를 보겠습니다.

fun ignoreFunc(func:(String, String)->String){
    println(func("Hello", "World!"))
}

fun JoinString(str1:String, str2:String):String = "${str1} ${str2}"

fun main(args:Array<String>){
    ignoreFunc({a, b->"${a} ${b}"})
    // ignoreFunc(::JoinString) // 일반 함수로 구성 가능
}
더보기

# 이 코드를 잘 모르겠어요

 ignoreFunc라는 함수가 존재합니다. 이 함수는 다른 함수를 인자로 받아들이게 됩니다. 받아들이는 함수는 매개 변수가 두 개 있습니다. 두 매개 변수 모두 String 자료형을 가지고 있죠. 마지막으로 반환 자료형도 String인 함수를 ignoreFunc 함수의 매개변수로 넣을 수 있습니다.

 

println(func("Hello", "World!"))
// ignoreFunc 함수의 내부 코드

 ignoreFunc 함수 내부의 println 함수가 있습니다. println 함수 안에 func 함수[# 매개 변수로 전달된 함수]가 실행됩니다. func 함수가 실행된 후 반환된 문자열 결과가 println 함수로 인해 출력됩니다.

 만일 사용자가 직접 만든 함수에서 둘 중 하나의 값만 사용하려 한다면 아래처럼 작성할 수 있을 것입니다.

fun ignoreFunc(func:(String, String)->String){
    println(func("Hello", "World!"))
}

fun JoinString(str1:String, str2:String):String = "${str2}"

fun main(args:Array<String>){
    ignoreFunc({a, b->"${b}"})
    // ignoreFunc(::JoinString) // 일반 함수로 구성 가능
}

 처음 함수와 무슨 차이가 있나요? 그렇습니다. 첫 번째 인자는 사용하지 않고 두 번째 인자만 사용한 값을 반환토록 하고 있습니다.

두 인자를 모두 쓴 왼쪽, 하나의 인자만 사용한 오른쪽

효과적으로 사용하기

 위에서 설명한 방법은 문제가 하나 있습니다. 다른 사람이 코드를 보았을 때 의도적으로 작성된 코드인지, 실수로 인자 하나를 사용하는 것을 잊고 작성한 것인지 모른다는 것입니다. 여러 사람들이 협업할 코드라면, 내가 평생 유지 보수할 코드가 아니라면 이 문제는 간단히 볼 수만도 없습니다.

 Kotlin은 그래서 이 문제를 피해 갈 수 있는 기술을 문법으로서 제공합니다.

fun ignoreFunc(func:(String, String)->String){
    println(func("Hello", "World!"))
}

fun main(args:Array<String>){
    ignoreFunc({_, b->"${b}"})
}

 이 코드를 실행해보면 이상 없이 실행됨을 알 수 있습니다. ignoreFunc 함수의 람다식의 첫 변수를 언더스코어[# "_", 언더-바로도 불립니다]로 변경했습니다. 만일 위 코드처럼 내가 작성한 코드를 다른 사람이 작성한 코드 안에서 실행해야 할 때, 그리고 타인이 내 함수로 넣은 인자들 중 사용하지 않을 값들이 있을 때는 이처럼 언더스코어를 사용하면 나중에 유지 보수하는데 도움이 됩니다. 작성자가 의도적으로 사용하지 않았다는 것을 주석을 달지 않고도 알 수 있으니까요.

 

주석을 읽지 않고도 의도를 알 수 있는 코드가 가장 좋은 코드다.

 이번에는 조금 더 빨리 끝났네요. 다음 포스트에서 뵙겠습니다!

 

# index

728x90
반응형

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

Kotlin: 17 람다식(Lambda Expression) part1. Basic  (0) 2020.08.23
Kotlin: 16 고차 함수  (0) 2020.07.22
Kotlin: 15 함수형 프로그래밍  (1) 2020.06.06
Kotlin: 14 인라인 함수와 익명 함수  (0) 2020.04.06
Kotlin: 13 예외 처리  (1) 2020.04.05
Comments