PS 문제를 하나씩 풀다 보니, 공부가 필요하다고 느낀 문법을 정리한 글입니다.
람다(lambda)는 이름 없이 사용할 수 있는 함수 표현식입니다.
보통 “익명 함수”와 비슷한 개념으로 이해하곤 하는데, Kotlin에서는 코드를 더 간결하게 작성할 때 자주 사용됩니다.
특히 컬렉션 처리나 고차 함수와 함께 사용할 때 람다의 장점이 잘 드러납니다.
예를 들어 리스트를 순회하면서 값을 변환하거나, 특정 조건에 맞는 값만 골라낼 때 람다를 사용하면 코드를 훨씬 짧고 읽기 쉽게 만들 수 있습니다.
즉, 람다는 단순히 짧게 쓰기 위한 문법이 아니라
함수를 값처럼 다루기 위한 Kotlin의 중요한 표현 방식이라고 볼 수 있습니다.
람다를 이해하려면 먼저 고차 함수를 알아두는 것이 좋습니다.
Kotlin의 함수는 일급 객체(first-class citizen)처럼 다뤄질 수 있습니다.
즉, 함수를 일반 값처럼 변수에 저장할 수도 있고, 다른 함수에 전달할 수도 있으며, 함수의 반환값으로 돌려줄 수도 있습니다.
이런 성질을 바탕으로 만들어지는 함수가 바로 고차 함수입니다.
고차 함수는 다음 둘 중 하나에 해당하는 함수입니다.
정리하면, Kotlin에서는 함수를 하나의 값처럼 다룰 수 있기 때문에
람다도 변수에 담거나, 다른 함수에 넘기거나, 반환값으로 사용할 수 있습니다.
일급 객체라는 말이 조금 어렵게 들릴 수 있지만,
쉽게 말하면 다른 값들처럼 자유롭게 다룰 수 있는 대상이라는 뜻입니다.
함수가 일급 객체처럼 다뤄진다는 것은 다음이 가능하다는 의미입니다.
예를 들어 아래처럼 사용할 수 있습니다.
fun main() {
// 함수를 변수에 저장
val apple = { fruit: String -> println("fruit : $fruit") }
apple("apple")
// 함수를 파라미터로 전달
val bucket = listOf("apple", "banana", "strawberry", "watermelon")
.fold("tomato") { acc, next -> "$acc, $next" }
println(bucket)
}
위 예제처럼 람다는 변수에 담을 수도 있고,
fold 같은 함수의 인자로 전달할 수도 있습니다.
여기서 apple("apple")처럼 바로 호출할 수 있는 이유는
람다가 함수처럼 동작하기 때문입니다.
invoke()로도 호출할 수 있지만, 처음에는 일반 함수처럼 호출된다고 이해하셔도 충분합니다.
람다는 다음과 같은 이유로 자주 사용됩니다.
다만 한 가지 주의할 점도 있습니다.
람다를 사용한다고 해서 항상 성능이 좋아지는 것은 아닙니다.
상황에 따라 객체 생성 비용이나 캡처 비용이 생길 수도 있기 때문입니다.
또 “지연 연산” 역시 람다 자체의 특징이라기보다,
Sequence처럼 지연 평가를 지원하는 구조와 함께 사용할 때 의미가 있습니다.
그래서 람다는 성능 최적화 도구라기보다
표현력을 높이고 코드를 깔끔하게 만드는 도구로 이해하는 편이 더 자연스럽습니다.
Kotlin의 람다는 보통 다음과 같은 형태로 작성합니다.
(val1: 타입, val2: 타입) -> 반환타입
예를 들면 아래와 같습니다.
() -> Int
(String) -> Unit
(Int, Int) -> Int
의미를 풀어보면 다음과 같습니다.
() -> Int : 매개변수는 없고 Int를 반환(String) -> Unit : String 하나를 받고 반환값은 없음(Int, Int) -> Int : Int 두 개를 받아 Int를 반환람다를 실제로 작성할 때는 보통 아래처럼 씁니다.
val sum = { a: Int, b: Int -> a + b }
println(sum(1, 2))
파라미터 타입은 문맥상 추론이 가능하면 생략할 수 있습니다.
val numbers = listOf(1, 2, 3)
val result = numbers.map { it * 10 }
println(result)
이 경우 it은 각 요소를 가리키는 기본 이름입니다.
Kotlin에서는 마지막 인자가 함수인 경우, 그 람다를 괄호 밖으로 뺄 수 있습니다.
이 문법을 자주 보게 되는데, 처음엔 조금 낯설어도 익숙해지면 훨씬 읽기 편합니다.
fun greeting(value: String, block: (String) -> Unit) {
block(value)
}
fun main() {
greeting("hello world") { println(it) }
}
위 코드는 greeting 함수의 마지막 인자인 block이 람다이기 때문에
괄호 밖으로 꺼내서 작성한 형태입니다.
이 문법 덕분에 Kotlin에서는 컬렉션 처리 코드도 자연스럽게 읽히는 경우가 많습니다.
람다는 컬렉션과 함께 사용할 때 특히 강력합니다.
예를 들어 여러 사람 중 가장 나이가 많은 사람의 나이를 구하고 싶다고 해보겠습니다.
data class Person(
val name: String,
val age: Int
)
fun main() {
val billGates = Person("Bill Gates", 30)
val markZuckerberg = Person("Mark Zuckerberg", 20)
val elonMusk = Person("Elon Musk", 10)
val maxAge = listOf(billGates, markZuckerberg, elonMusk)
.maxOf { person -> person.age }
println(maxAge)
}
30
위 코드에서 maxOf는 각 요소에서 비교 기준이 될 값을 꺼내야 하는데,
그 기준을 람다로 전달하고 있습니다.
즉, person -> person.age는
“각 사람 객체에서 age를 꺼내 비교 기준으로 사용하라”는 의미입니다.
이처럼 람다는 컬렉션의 각 요소를 어떻게 처리할지 간단하게 표현할 수 있게 도와줍니다.
이번 글에서는 Kotlin의 람다에 대해 정리해보았습니다.
람다는 이름 없는 함수 표현식이며,
Kotlin에서 함수를 값처럼 다룰 수 있게 해주는 중요한 문법입니다.
특히 고차 함수와 함께 사용할 때 진가가 드러나고,
컬렉션 처리 코드도 훨씬 간결하게 만들 수 있습니다.
정리하면 람다는 다음과 같이 이해하시면 됩니다.
처음에는 문법이 조금 낯설 수 있지만,
map, filter, forEach 같은 함수와 함께 계속 써보면 훨씬 익숙해집니다.
LambdasCoding conventions