일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Programming
- c#
- 지식나눔강좌
- tipssoft
- Javascript
- 포인터
- 리뷰
- Tips강좌
- Direct2D
- Visual Studio
- Desktop
- 배열
- 백준
- 연산자
- 문법
- doit코틀린프로그래밍
- VS ERROR
- Tips프로그래밍강좌
- 김성엽
- 티스토리
- Kotlin
- Windows
- c++
- CS
- 알고리즘
- 함수
- c
- 이지스퍼블리싱
- Win32
- 프로그래밍
- Yesterday
- Today
- Total
F.R.I.D.A.Y.
Kotlin: 18 람다식(Lambda Expression) part2. Advance 본문
이전 시간에 람다식의 기초 부분을 배웠다면 이번엔 람다식을 조금 더 심도 있게 다뤄보겠습니다.
람다식의 이용
인자에 사용하기
인자에 사용하는 방법은 여러 가지가 있습니다. 이번에 소개하는 문법은 인자의 마지막에 들어가는 람다식은 소괄호 밖으로 빼낼 수 있다는 것입니다.
람다식 두 개를 인자로 받는 테스트 함수를 작성해보겠습니다.
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
'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 |