익명 함수
익명 함수 (Anonymous Functions)
익명 함수(anonymous function) — 람다(lambda) 라고도 부르죠 — 는 고차 함수(higher-order function)에 인자로 전달되는 코드 블록이에요. 위키백과에서는 익명 함수를 “식별자에 묶이지 않은 함수 정의”라고 정의해요.
예를 들어 이런 리스트가 있다고 할게요.
val ints = List(1, 2, 3)
List 클래스의 map 메서드와 여러분만의 커스텀 익명 함수를 사용하면, ints의 각 요소를 2배로 만든 새 리스트를 만들 수 있어요.
val doubledInts = ints.map(_ * 2) // List(2, 4, 6)
주석에서 보이듯이 doubledInts는 List(2, 4, 6)이라는 리스트를 담아요. 이 예시에서 코드의 이 부분이 익명 함수랍니다.
_ * 2
이건 “주어진 요소를 2로 곱하라”는 뜻을 짧게 표현한 거예요.
더 긴 형태 (Longer forms)
Scala에 익숙해지면 함수 안의 한 지점에서 변수 하나를 쓰는 익명 함수를 쓸 때 저 형태를 항상 쓰게 될 거예요. 하지만 원한다면 더 긴 형태로도 쓸 수 있어요. 그래서 이런 코드를 쓰는 것 말고도
val doubledInts = ints.map(_ * 2)
이런 형태들로도 똑같이 표현할 수 있어요.
val doubledInts = ints.map((i: Int) => i * 2)
val doubledInts = ints.map((i) => i * 2)
val doubledInts = ints.map(i => i * 2)
이 줄들은 전부 완전히 같은 뜻이에요. ints의 각 요소를 2배로 만들어 새 리스트 doubledInts를 만든다는 거죠. (각 형태의 문법은 잠시 후에 설명할게요.)
Java에 익숙하다면, 이 map 예시들이 다음 Java 코드와 같다는 걸 알아두면 도움이 될 거예요.
List<Integer> ints = List.of(1, 2, 3);
List<Integer> doubledInts = ints.stream()
.map(i -> i * 2)
.collect(Collectors.toList());
익명 함수 줄이기 (Shortening anonymous functions)
명시적으로 쓰고 싶다면, 익명 함수를 이런 긴 형태로 작성할 수 있어요.
val doubledInts = ints.map((i: Int) => i * 2)
그 표현식 안의 익명 함수는 바로 이것이에요.
(i: Int) => i * 2
이 문법에 익숙하지 않다면 => 기호를 변환기(transformer)로 생각하면 도움이 돼요. 그 표현식이 기호 왼쪽의 파라미터 리스트(i라는 Int 변수)를 기호 오른쪽의 알고리즘(이 경우엔 Int를 2배로 만드는 표현식)을 사용해 새로운 결과로 변환 하니까요.
그 표현식 줄이기 (Shortening that expression)
이 긴 형태는 다음 단계를 거쳐 줄일 수 있어요. 먼저 가장 길고 가장 명시적인 형태부터 다시 볼게요.
val doubledInts = ints.map((i: Int) => i * 2)
Scala 컴파일러가 ints 안의 데이터로부터 i가 Int임을 추론할 수 있기 때문에, Int 선언은 제거할 수 있어요.
val doubledInts = ints.map((i) => i * 2)
인자가 하나뿐이므로 파라미터 i를 감싸는 괄호도 필요 없어요.
val doubledInts = ints.map(i => i * 2)
Scala에서는 함수 안에 파라미터가 한 번만 등장할 때 변수 이름 대신 _ 기호를 쓸 수 있기 때문에, 코드를 더 단순하게 만들 수 있어요.
val doubledInts = ints.map(_ * 2)
더 짧게 (Going even shorter)
다른 예시에서는 익명 함수를 더 단순화할 수도 있어요. 예를 들어 가장 명시적인 형태에서 시작해서, List 클래스의 foreach 메서드와 함께 이 익명 함수를 사용해 ints의 각 요소를 출력할 수 있어요.
ints.foreach((i: Int) => println(i))
이전과 마찬가지로 Int 선언은 필요 없고, 인자가 하나뿐이므로 i 주변의 괄호도 필요 없어요.
ints.foreach(i => println(i))
i가 함수 본문에서 한 번만 사용되므로, _ 기호로 표현식을 더 단순화할 수 있어요.
ints.foreach(println(_))
마지막으로, 익명 함수가 인자 하나를 받는 메서드 호출 하나로 이뤄져 있다면, 인자를 굳이 이름 붙여 명시할 필요가 없어요. 그래서 마침내 메서드 이름(여기서는 println)만 쓰면 됩니다.
ints.foreach(println)
더 알아보기 (Learn more)
- 원문: Scala 3 Book — Anonymous Functions
- 고차 함수: Higher-Order Functions
- 함수 변수: Function Variables