231219 TIL #272 Kotlin #7 람다 - 1

김춘복·2023년 12월 19일
0

TIL : Today I Learned

목록 보기
272/571

Today I Learned

Kotlin in Action 이어서 람다에 대해 공부했다.


람다

  • 람다 식(lambda expression) : 다른 함수에 넘길 수 있는 작은 코드 조각.
    값처럼 여기저기 전달할 수 있는 동작의 모음.

  • 람다식을 사용하면 함수를 선언할 필요 없이 코드 블록을 직접 함수의 인자로 전달해 코드를 간결하게 유지할 수 있다.

  • 객체의 리스트에서 특정 요소의 최대값을 구할 때, 복잡한 for문 필요없이 아래처럼 간단하게 람다와 컬렉션의 함수를 이용해 구할 수 있다.

fun main() {
    val people = listOf(Person("kim", 30), Person("Ryu", 25))
    println(people.maxBy { p: Person -> p.age }) // 1번
    println(people.maxBy { p -> p.age }) // 2번
    println(people.maxBy { it.age }) // 3번
    println(people.maxBy(Person::age)) // 4번
	// 모두 Person(name=kim, age=30)로 결과는 같다.
}

람다 식의 문법

{ x: Int, y: Int -> x + y }
  • 항상 중괄호 사이에 위치하고 파라미터와 본문을 ->화살표가 구분해준다.

  • 컴파일러는 로컬 변수처럼 람다 파라미터의 타입도 추론할 수 있으므로 위의 예시에서 1번처럼 명시할 필요는 없다. 2번처럼 써도 된다.

  • 파라미터의 이름을 디폴트인 it으로 바꾸면 3번처럼 더 간단히 사용할 수 있다.
    단, 람다가 중첩되는 경우 가독성 측면에서 it보단 파라미터를 명시하는 편이 낫다.

  • 코틀린 람다가 자바에서와 다른점은 파이널 변수가 아닌 변수에 접근이 가능하다.
    람다 안에서 바깥의 변수(not final)를 변경해도 된다.
    이처럼 람다 안에서 사용하는 변수를 람다가 포획(capture)한 변수라 한다.

멤버 참조(member reference)

코틀린에서는 자바8과 마찬가지로 함수를 값으로 바꿀 수 있다. 이중콜론 ::을 사용한다.
val getAge = Person::age
클래스(Person)와 멤버(age)를 ::로 구분하면 된다. 예시에서 4번과 같다.

  • 참조 대상이 함수든 프로퍼티든 뒤에 괄호는 넣으면 안된다.

  • (다른 클래스의 멤버가 아닌) 최상위에 선언된 함수나 프로퍼티를 참조할 수도 있다.
    이 경우 클래스 이름을 생략하고 바로 ::멤버명 으로 한다.

  • 생성자 참조 : 클래스 생성 작업을 연기하거나 저장해둘 수 있다.
    ::클래스명으로 생성자를 참조한다.

data class Person(val name: String, val age: Int)

    val createPerson = ::Person
    val p = createPerson("Park", 15)
  • 클래스명::확장함수명 이 방식으로 확장함수도 참조가 가능하다.

컬렉션 함수형 API

1. filter & map

컬렉션 활용 시 기반이 되는 함수

    val list = listOf(1,2,3,4,5,6)
    println(list.filter { it%2==0 }) // [2, 4, 6]
    println(list.map { it*it }) // [1, 4, 9, 16, 25, 36]
    println(people.map { it.name })  // [kim, Ryu] Person -> String으로 변했으므로 map
    println(people.filter { it.age>=30 }.map(Person::name)) // [kim]
  • filter : 주어진 술어(조건, predicate)를 만족하는 원소만 남는다.
    원치 않는 원소는 제거가 가능하지만, 원소를 변환할 수는 없다.
  • map : 주어진 람다를 컬렉션의 각 원소에 적용한 결과를 모아 새 컬렉션을 만든다.

2. all, any, count, find

컬렉션의 모든 원소가 어떤 조건을 만족하는지 판단하는 연산을 한다. 컬렉션에 술어 적용.

  • all : 모든 원소가 술어를 만족하는지 판별
    any : 술어를 만족하는 원소가 하나라도 있는지 판별
    count : 술어를 만족하는 원소의 개수를 반환
    find : 술어를 만족하는 첫번째 원소를 반환(없으면 Null 반환)

3. groupBy

컬렉션의 모든 원소를 어떤 특성에 따라 여러 그룹으로 나누고 싶을 때 사용

  • 리스트를 Int 요소 하나로 그룹화할 경우, Map<Int, List<Person>>으로 나온다.
    val people = listOf(Person("kim", 31), Person("Ryu", 25), Person("Lee", 31), Person("Son", 21))
    val group = people.groupBy { it.age }
    println(group) 
    // 출력 : {31=[Person(name=kim, age=31), Person(name=Lee, age=31)], 25=[Person(name=Ryu, age=25)], 21=[Person(name=Son, age=21)]}

4. flatMap

먼저 인자로 주어진 람다를 컬렉션의 모든 객체에 적용하고, 결과로 얻어지는 여러 리스트를 한데 모은다.

class Book(val title: String, val authors: List<String>)
books.flatMap{ it.authors }.toSet() // 모든 저자의 집합
  • 특별히 변환할 내용이 없으면 flatMap() 대신 flatten()을 사용할 수 있다.

profile
Backend Dev / Data Engineer

0개의 댓글