[코인액] 람다가 무엇이오?

Yerin·2023년 3월 5일

코틀린

목록 보기
7/11
post-thumbnail

테코톡을 신청하면서 kotlin의 scope function들을 주제로 잡았는데,
나는 lambda가 뭔지도 모르는데 이걸 발표한다고 한거였다 ㅋ_ㅋ
그래서 또 건너 뛰어서 ~ 읽어보는 ~ Kotlin In Action 5장. 람다로 프로그래밍 입니다 🤗


람다

기본적으로 다른 함수에 넘길 수 있는 작은 코드 조각을 뜻한다.

람다를 사용하면 공통 코드 구조를 쉽게 라이브러리 함수로 뽑아낼 수 있다.

람다 식과 멤버 참조

람다가 왜 좋아요? 코드 블록을 함수 인자로 넘기기

이전에 자바에서는 무명 내부 클래스를 통해 일련의 동작을 변수에 저장하거나 다른 함수에 넘겨야 하는 작업들을 해결했었다. 무명 내부 클래스를 사용하면 코드를 함수에 넘기거나 변수에 저정할 수 있기는 하지만 상당히 번거롭다.

이와 달리 함수형 프로그래밍에서는 함수를 값처럼 다루는 접근 방법을 택해 이러한 문제를 해결한다. 클래스를 선언하고, 그 클래스의 인스턴스를 함수에 넘기는 대신 함수형 언어에서는 함수를 직접 다른 함수에 전달할 수 있다.

람다 식을 이용하면 코드가 더욱 간결해진다.
람다 식을 사용하면 함수를 선언할 필요가 없고 코드 블록을 직접 함수의 인자로 전달 가능하다.

button.setOnClickListener (new OnClickListener () {
	@Override
   	public void onClick(View view) {
    	/*동작*/
    }
});

위와 같이 무명 내부 클래스를 선언하느라 번잡스러워진 코드를 람다를 사용하려 클릭 시 동작을 간단히 기술할 수 있다.

button.setOnClickListener { /*동작*/ }

람다와 컬렉션

이전 자바에서는 람다가 존재하지 않아 컬렉션을 편리하게 처리할 수 있는 라이브러리를 제공하기 힘들었고, 필요한 기능들을 직접 작성하는 경우가 많았다.
만약 람다를 사용하지 않는다면, 사람들 중 연장자를 찾기 위해서 최고령과 연장자 변수를 선언하고 루프를 돌렸을 것이다.

하지만 람다식을 사용하여 라이브러리 함수를 활용하면 다음과 같이 한줄로 끝낼 수 있다.

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

val people = listOf(Person("ringring", 26), Person("movie", 26))
people.maxBy { it.age }
// 이런식으로 단지 함수나 프로퍼티를 반환하는 역할을 수행하는 람다는 멤버 참조로 대치할 수 있다.
people.maxBy(Person::age)

람다 식 문법

{ 인자 목록 -> 람다 본문 }

people.maxBy({ p: Person -> p.age })
// 코틀린에서는 함수 호출 시 맨 뒤에 있는 인자가 람다 식이라면 그 람다를 괄호 밖으로 빼낼 수 있다.
people.maxBy() { p: Person -> p.age }
// 람다가 어떤 함수의 유일한 이자이고, 괄호 뒤에 람다를 썼다면 호출 시 괄호를 없애도 좋다. 
people.maxBy { p: Person -> p.age }
// 컴파일러는 람다 파라미터의 타입도 추론 가능하기 때문에 파라미터 타입을 명시할 필요가 없다
people.maxBy { p -> p.age }

people.joinToString(separator = " ", transform = { p: Person -> p.name }
people.joinToString(" ") { p: Person -> p.name }

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

자바에서와 달리 코틀린의 람다는

  • 파이널 변수가 아닌 변수(immutable)에 접근 가능하다. (-> Ref 클래스 인스턴스 이용)
  • 람다 안에서 람다 바깥의 변수를 변경할 수 있다.

포획한 변수가 있는 람다를 저장해서 함수가 끝난 뒤에 실행해도 람다의 본문 코드는 여전히 포획한 변수를 읽거나 쓸 수 있다.
HOW?

  • 파이널 변수를 포획한 경우에는 람다 코드를 변수 값과 함께 저장한다.
  • 파이널이 아닌 변수를 포획한 경우에는 변수를 특별한 래퍼로 감싸서 나중에 변경하거나 읽을 수 있게 한 다음, 래퍼에 대한 참조를 람다 코드와 함께 저장한다.

주의
람다를 이벤트 핸들러나 다른 비동기적으로 실행되는 코드로 활용하는 경우 함수 호출이 끝난 다음 로컬 변수가 변경될 수도 있다.

fun tryToCountButtonClicks(button: Button): Int {
	var clicks = 0
    button.onClick { clicks++ }
    return clicks
}

위의 함수는 항상 0을 반환한다. onClick 핸들러는 호출될 떄마다 clicks 값을 증가시키지만 그 값을 변경을 관찰 할 수 없다. 핸들러는 함수가 clicks가 반환된 후 호출되기 때문이다.
-> 제대로 구현하기 위해서는 카운터 변수를 함수의 내부가 아니라 클래스의 프로퍼티나 전역 프로퍼티 등의 위치로 빼내서 나중에 변수의 변화를 살펴볼 수 있게 해야한다.


멤버 참조

멤버 참조는 프로퍼티나 메서드를 단 하나만 호출하는 값을 만들어준다.
::는 클래스 이름과 참조하려는 멤버(프로퍼티나 메서드) 이름 사이에 위치한다.

people.maxBy(Person::age)
people.maxBy { p -> p.age }
people.maxBy { it.age }
profile
𝙸 𝚐𝚘𝚝𝚝𝚊 𝚕𝚒𝚟𝚎 𝚖𝚢 𝚕𝚒𝚏𝚎 𝙽𝙾𝚆, 𝙽𝙾𝚃 𝚕𝚊𝚝𝚎𝚛 !

0개의 댓글