익명 함수 (Anonymous Functions)
익명 함수란, 이름처럼 익명, 즉 이름을 가지지 않는 함수를 의미합니다.
// 익명함수
val sum = fun(x: Int, y: Int): Int {
return x + y
}
fun main() {
println(sum(1, 2))
}
// 출력
>>> 3
익명함수는 위와 같이 이름없이 정의되는 함수입니다.
앞서 (1)편에서 언급했듯이, 함수는 일급객체이기 때문에 변수에 할당이 가능합니다.
자바스크립트에서 많이 본 형태이죠?
저는 JS를 별로 안다뤄본 탓에 익숙해지는데 조금 걸렸습니다.
fun main() {
val testP2 = ::testPrintln
testP2()
}
fun testPrintln() {
println("Hello World!")
}
또한, 미리 정의해놓은 함수를 :: 키워드를 사용해 변수에 할당할 수도 있습니다.
람다식 (Lambda Expression)
// 람다식
val sum = { x: Int, y: Int -> x + y }
fun main() {
val result = sum(1, 2)
println(result)
}
// 출력
>>> 3
람다식을 사용해 더 간단하게 표현할 수 있습니다.
위 람다식은 val sum = { 매개변수 : 타입, 매개변수2: 타입 -> 반환값 } 으로 표현하였습니다.
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val sum = { x: Int, y: Int -> x + y } // 타입 추론을 이용해 선언부의 타입 생략 가능
val sum: (Int, Int) -> Int = { x, y -> x + y } // 타입 추론을 사용해 매개변수의 타입 생략 가능
람다식은 첫 번째 식이 가장 기본적인 상태이며
매개변수 부분에 타입이 명시되어 있다면 선언부를 생략,
선언부에 타입이 명시되어 있다면 매개변수 타입을 생략할 수 있습니다.
저는 두 번째 형태를 가장 즐겨 사용합니다.
인자가 하나일 땐 it 키워드로 암시적으로 받을 수 있다
fun main() {
val plus: (a: Int) -> Int = { a -> a + 1 }
println(plus(1))
}
// 출력
2
==================================================
fun main() {
val plus: (Int) -> Int = { it + 1 }
println(plus(1))
}
// 출력
2
두 코드의 plus는 모두 받은 인자를 +1 하여 return 해주는 람다식입니다.
매개변수가 하나일 땐 it 키워드를 사용해 암시적으로 받을 수 있습니다.
함수를 파라미터로 전달
// 함수를 파라미터로 전달
fun function(a: Int, b: Int, func: (Int, Int) -> Int): Int {
return func(a, b)
}
fun main() {
val sumFunc = { a: Int, b: Int -> a + b } // 더하는 람다식
val multiFunc = { a: Int, b: Int -> a * b } // 곱하는 람다식
val sumResult = function(10, 20, sumFunc)
val multiResult = function(10, 20, multiFunc)
val subResult = function(10, 20, { a, b -> a - b }) // 빼는 람다식
println(sumResult)
println(multiResult)
println(subResult)
}
// 출력
>>> 30
200
-10
function 함수는 a, b와 (Int, Int) -> Int 형식의 람다함수를 인자로 받습니다.
매개변수가 Int 2개, return이 Int인 형식의 람다식이면 다 받을 수 있다는 의미이죠.
그리고 a, b와 람다함수를 갖고 로직을 수행합니다.
fun main()에서
sumFunc는 두 인자를 더하는 람다식,
multiFunc는 두 인자를 곱하는 람다식입니다.
이와 같은 람다식은 마치 값을 변수에 할당하듯 함수를 변수에 할당한 후
변수를 매개변수로 넘겨주듯이 매개변수로써 함수를 넘겨줄 수 있습니다.
물론, 변수에 할당하지 않은 채 subResult와 같이 바로 람다식을 넣어줄 수도 있습니다.
printIf 구현하기
// lambda function
fun String.printIf(func: (Char) -> Boolean) {
this.forEach {
if (func(it)) print(it)
}
}
fun main() {
val str = "Hello World Koltin"
str.printIf { it.isUpperCase() }
}
// 출력
>>> HWK
filter { }, countIf { }, sumIf { } 등 추후 컬렉션 프레임워크 파트에 나올 이들도
코틀린 람다식에 기반을 두고 있습니다.
저는 앞서 나온 확장함수와 람다식을 사용해
조건에 맞는 문자열을 뽑아 출력하는 printIf를 구현해봤습니다.
꽤 간단히 구현할 수 있었는데요.
확장함수 편에서 배운 String.(메소드명)을 통해, String의 메소드를 추가해줬고,
인자로써 func: (Char) -> Boolean 형식의 람다식을 받을 수 있도록 했습니다.
그리고 String 내 문자열을 하나씩 돌리며 (this.forEach)
만약 매개변수로 밭은 함수에 인자로 현재 문자을 넣었는데 (func(it))
이게 true를 반환한다면 if(func(it))
출력한다 print(it) 입니다.
forEach 함수는 for문 편에서 한 번 나온 녀석이였는데,
각 원소를 하나씩 순회하는 반복문입니다.
fun String.printIf(func: (Char) -> Boolean) {
// val str = "Hello World Kotlin"
// str.printIf 와 같이 사용하면 str를 this로 받을 수 있음.
this.forEach {
// str내 문자 하나하나를 it으로 받음.
// func(it)는 func의 인자로 it를 넣어서 실행한 결과를 받음.
// str.printIf { it.isUpperCase() } 라고 사용하면
// func(it)는 it.isUpperCase()를 실행한 결과를 받음.
if (func(it)) print(it)
}
}
주석을 달아 설명을 보태봤습니다.
함수형 프로그래밍편 마침
지금까지 함수형 프로그래밍에 관한 소개와 간단한 실습을 해봤습니다.
으음... 함수형 프로그래밍이 뭔지, 어떻게 구현하는지는 대강 알겠는데
어떻게 활용해야 하는지는 아직 잘 모르겠습니다.
나중에 함수형 프로그래밍을 잘 활용하게 된다면,
함수형 프로그래밍에 관한 글을 하나 더 올려야겠네요.
'코틀린 파헤치기 > 3부. 코틀린 고급' 카테고리의 다른 글
[코파기 3부] 8. 코틀린과 컬렉션 프레임워크 (2) : Map (0) | 2023.03.27 |
---|---|
[코파기 3부] 7. 코틀린과 컬렉션 프레임워크 (1) : Set (0) | 2023.03.27 |
[코파기 3부] 5. 코틀린과 함수형 프로그래밍 (1) : 함수형 프로그래밍이란? (0) | 2023.03.24 |
[코파기 3부] 4. 코틀린과 스코프 함수 : apply, run, with, also, let (0) | 2023.03.22 |
[코파기 3부] 3. 코틀린과 예외처리 : try - catch, finally (0) | 2023.03.22 |