[코틀린 인 액션] CH5 람다로 프로그래밍

0

코틀린 인 액션

목록 보기
6/13
post-thumbnail

이 포스팅은 <<Kotlin in Action>>, 드미트리 제메로프 & 스베트라나 이사코바, 에이콘출판사(2017)을 읽고 개인 학습용으로 정리한 글입니다.

[코틀린 인 액션] CH5 람다로 프로그래밍

5.1 람다 식과 멤버 참조

5.1.1 람다 소개: 코드 블록을 함수 인자로 넘기기

  • 함수형 언어에서는 함수를 직접 다른 함수에 전달 가능

5.1.2 람다와 컬렉션

  • maxBy: 가장 큰 원소를 찾기 위해 비교에 사용할 값을 돌려주는 함수를 인자로 받음
val people = listOf(Person("Alice", 29), Person("Bob", 31))

//람다를 이용해 컬렉션 검색하기
println(people.maxBy{it.age})
//멤버 참조를 사용해 컬렉션 검색하기
println(people.maxBy(Person::age))
  • 단지 함수나 프로퍼티를 반환하는 역할을 수행하는 람다 -> 멤버 참조로 대치 가능

5.1.3 람다 식의 문법

{x: Int, y:Int -> x+y}
  • 코틀린 람다식은 항상 중괄호로 둘러싸여 있음

  • 인자 목록 주변에 괄호 없음
    화살표(->)가 인자 목록과 함수 본문 구분

  • run: 인자로 받은 람다를 실행해주는 라이브러리 함수
    -> 코드의 일부분을 블록으로 둘러싸 실행할 필요가 있을 때 사용

  • 실행 시점에 코틀린 람다 호출에 무가 비용X, 프로그램의 기본 구성 요소와 비슷한 성능

  • 함수 호출 시 맨 뒤에 있는 인자가 람다 식 -> 그 람다를 괄호 밖으로 빼낼 수 O

  • 람다가 어떤 함수의 유일한 인자이고 괄호 뒤에 람다 식 -> 빈 괄호 생략 가능

  • 컴파일러는 람다 파라미터의 타입도 추론 가능
    -> 처음에는 타입을 쓰지 않고 람다 작성 -> 컴파일러가 타입 추론 못하는 경우에만 타입 명시

  • 람다릐 파라미터 디폴트 이름 it
    -> 람다의 파라미터 하나 뿐이고 컴파일러가 그 타입을 추론할 수 있는 경우 사용 가능

5.1.4 현재 영역에 있는 변수애 접근

  • 람다를 함수 안에 정의하는 경우
    -> 함수의 파라미터 접근 가능
    -> 람다 정의의 앞에 선언된 로컨 변수 접근 가능

  • forEach: 컬렉션의 모든 원소에 대해 람다 호출

  • 람다에서 람다 밖 함수에 있는 변수 변경 가능
    -> 람다 안에서 사용하는 외부 변수: 람다가 포획한 변수

  • 기본적으로 함수 안에 정의된 로컬 변수의 생명 주기: 함수가 반환되면 끝남

  • 어떤 함수가 자신의 로컬 변수를 포획한 람다를 반환하거나 다른 변수에 저장하는 경우: 로컬 변수의 생명주기와 함수의 생명주기 달라질 수 O
    -> ⭐포획한 변수가 있는 람다를 저장해서 함수가 끝난 뒤에 실행해도 람다의 본문 코드는 여전히 포획한 변수를 읽거나 쓸 수 O

  • ex. 람다를 이벤트 핸들러나 다른 비동기적으로 실행되는 코드로 활용하는 경우
    -> 함수 호출이 끝난 다음에 로컬 변수 변경될 수 O

fun tryToCountButtonClicks(button: Button): Int{
	var clicks = 0
    button.onClick{ clicks++ }
    return clicks
}
  • 위 함수는 항상 0을 반환
    -> ⭐핸들러는 tryToCountButtonClicks가 clicks를 반환한 다음 호출되기 때문
    -> 클릭 횟수를 세는 카운터 변수를 함수 내부가 아닌 클래스의 프로퍼티나 전역 프로퍼티 등의 위치로 빼야

5.1.5 멤버 참조

  • 멤버 참조: 프로퍼티나 메서드를 단 하나만 호출하는 함수 값을 만들어줌
    -> 클래스::멤버
    -> 참조 대상이 함수인지 프로퍼티인지와 관계 없이 멤버 참조 뒤에 괄호 없음
  • 최상위에 선언된 함수/프로퍼티 참조 가능
    -> ::멤버

  • 생성자 참조: 클래스 생성 작업을 연기하거나 저장해둘 수 O
    -> ::클래스 이름

  • 확장 함수도 멤버 함수와 똑같은 방식으로 참조 가능

  • 바운드 멤버 참조: 멤버 참조를 생성할 때 클래스 인스턴스를 함께 저장 -> 나중에 그 인스턴스에 대해 멤버 호출

5.2 컬렉션 함수형 API

5.2.1 필수적인 함수: filter와 map

  • filter 함수: 컬렉션을 이터레이션하면서 주어진 람다에 각 원소 넘겨 람다가 true를 반환하는 원소만 모음
    (참/거짓을 반환하는 함수: predicate)

  • map 함수: 주어진 람다를 컬렉션의 각 원소에 적용한 결과를 모아서 새 컬렉션을 만든다

  • 맵의 경우 키와 값을 처리하는 함수 따로 존재
    -> filterKeys, mapKeys, filterValues, mapValues

5.2.2 all, any, count, find: 컬렉션에 술어 적용

  • find 함수: 조건을 만족하는 첫 번째 원소 반환, 만족하는 원소 전혀 없는 경우 null 반환
    -> firstOrNull과 같음

  • count와 size: 원소의 개수만을 추적하는 경우 count가 훨씬 더 효율적

5.2.3 groupBy: 리스트를 여러 그룹으로 이루어진 맵으로 변경

  • groupBy의 결과 맵
    -> Key: 컬렉션의 원소를 구분하는 특성
    -> Value: 키 값에 따른 각 그룹(List)

5.4.2 flatMap과 flatten: 중첩된 컬렉션 안의 원소 처리

  • flatMap 함수:
    -> 1. 인자로 주어진 람다를 컬렉션의 모든 객체에 적용(map)
    -> 2. 람다를 적용한 결과 얻어지는 여러 리스트를 하나의 리스트로 모음(flatten)

  • flatten 함수: 단순히 리스트를 평평하게 펼침(= 여러 리스트를 하나의 리스트로 모음)

5.3 지연 계산(lazy) 컬렉션 연산

  • 컬렉션 함수를 연쇄하면 매 단계마다 계산 중간 결과를 새로운 컬렉션에 임시로 담는다

  • 시퀸스를 사용하면 중간 임시 컬렉션 사용 없이 컬렉션 연산 연쇄 가능

  • Sequence 안에는 iterator라는 단 하나의 메서드만 있다

  • 시퀸스의 원소는 필요할 때 비로소 계산됨(=지연 계산)
    -> 계산을 실행하게 만들려면 최종 시퀸스의 원소를 하나씩 이터레이션하거나 최종 시퀸스를 리스트로 변환

5.3.1 시퀸스 연산 실행: 중간 연산과 최종 연산

  • 중간 연산: 다른 시퀸스 반환
    -> 항상 지연 계산 됨

  • 최종 연산: 결과 반환
    -> 최종 연산을 호출하면 연기됐던 모든 계산 수행됨

  • ⭐시퀀스의 경우 모든 연산은 각 원소에 대해 순차적으로 적용됨

5.3.2 시퀸스 만들기

  • 컬렉션에 대해 asSequence() 호출
    -> 모든 컬렉션에 대해 호출 가능

  • generateSequence 함수 사용

  • ⚡시퀸스를 사용하는 일반적인 용례: 객체의 조상으로 이루어진 시퀸스

5.4 자바 함수형 인터페이스 사용

  • 함수형 인터페이스/SAM 인터페이스
    (SAM: 단일 추상 메서드)

  • 코틀린은 함수형 인터페이스를 인자로 취하는 자바 메서드를 호출할 때 람다를 넘길 수 있다

5.4.1 자바 메서드에 람다를 인자로 전달

  • 함수의 인터페이스 구현으로 객체를 넘겨주는 경우
    -> 메서드를 호출할 때마다 새로운 객체 생성됨

  • 함수의 인터페이스 구현으로 람다를 넘겨주는 경우
    -> 람다에 대응하는 무명 객체 메서드를 호출할 때마다 반복 사용
    -> but 람다가 주변 영역의 변수를 포획하는 경우 반복 사용 X, 새로운 인스턴스 생성됨

  • 코틀린 1.0에서 인라인(inline)되지 않은 모든 람다 식은 무명 클래스로 컴파일됨
    -> 람다가 변수를 포획하면 무명 클래스 안에 포획한 변수를 저장하는 필드 생김
    -> 매 호출마다 그 무명 클래스의 인스턴스 새로 만든다

5.4.2 SAM 생성자: 람다를 함수형 인터페이스로 명시적으로 변경

  • SAM 생성자의 이름은 사용하려는 함수형 인터페이스의 이름과 같다

  • 람다 안에서 this는 그 람다를 둘러싼 클래스의 인스턴스를 가리킨다

5.5 수신 객체 지정 람다: with과 apply

  • 수신 객체를 명시하지 않고 람다의 본문 안에서 다른 객체의 메서드를 호출할 수 있게 함

5.5.1 with 함수

  • with 함수: 파라미터 2개
    -> 첫번째 인자로 받은 객체를 두번째 인자로 받은 람다의 수신 객체로 만든다
    -> 인자로 받은 람다 본문에서 this로 수신 객체 접근 가능

  • 람다의 결과 대신 수신 객체가 필요한 경우 apply 함수 사용

5.5.2 apply 함수

  • apply 함수는 거의 with와 같지만, 항상 자신에게 전달된 객체를 반환

  • 객체의 인스턴스를 만들면서 즉시 프로퍼티 중 일부를 초기화해야하는 경우 유용

profile
Be able to be vulnerable, in search of truth

0개의 댓글