리스트에서 짝수만 골라내거나, 정렬 기준을 잠깐 바꾸거나, 버튼 클릭 때 한 줄짜리 처리를 붙이고 싶을 때마다 별도의 Function 을 하나씩 만들어 두면 클래스가 금방 지저분해집니다. 이름 붙일 필요도 없는 한 번짜리 함수를 그 자리에서 바로 써 넣는 문법이 람다 식(무명 함수)입니다.

람다를 형상화한 빛나는 고리 모양 일러스트

광고

람다 함수 관련 삽화

VB.NET 람다는 이렇게 생겼다

C# 에서는 (x, y) => x + y 처럼 화살표로 쓰지만, VB.NET 은 키워드로 씁니다. 값을 돌려주면 Function, 돌려주지 않으면 Sub 입니다.

Dim add = Function(x As Integer, y As Integer) x + y
Dim square = Function(x As Integer) x * x
Dim show = Sub(msg As String) Console.WriteLine(msg)

Console.WriteLine(add(3, 4))   ' 7
show("완료")

한 줄짜리 Function 람다는 Return 을 쓰지 않고 식만 적습니다. 그 식의 값이 곧 반환값입니다. 매개변수 형식은 넣는 곳에서 추론이 되면 생략해도 됩니다. 아래 LINQ 예제들이 대부분 형식을 생략하고 쓰는데, List(Of Integer) 에 붙이니까 x 가 Integer 라는 걸 컴파일러가 알기 때문입니다.

람다 함수 관련 삽화

물방울 모양의 고리가 빛나는 코드 배경 일러스트

예를 들어 리스트의 각 요소를 제곱한 새 리스트가 필요하면 Select 에 람다를 넘깁니다.

Dim numbers As List(Of Integer) = New List(Of Integer) From {5, 2, 8, 1, 9}
Dim squares = numbers.Select(Function(x) x * x).ToList()   ' 25, 4, 64, 1, 81

리스트 다룰 때 자주 쓰는 다섯 가지

람다가 가장 빛을 보는 곳은 컬렉션 처리입니다. 정렬, 걸러내기, 다른 값으로 바꾸기, 누적 계산이 거의 한 줄씩으로 끝납니다.

람다 함수 관련 삽화

정렬 — List.Sort 에 두 값을 비교하는 람다를 넘깁니다. 음수면 앞, 양수면 뒤입니다.

Dim numbers As List(Of Integer) = New List(Of Integer) From {5, 2, 8, 1, 9}
numbers.Sort(Function(x, y) x.CompareTo(y))

오름차순이라 사실 numbers.Sort() 와 결과가 같습니다. 내림차순이 필요하면 y.CompareTo(x) 로 뒤집고, 객체 리스트라면 Function(a, b) a.Price.CompareTo(b.Price) 처럼 비교할 속성만 바꿔 주면 됩니다. 이 메서드는 원래 리스트 자체의 순서를 바꾼다는 점도 기억해 두세요.

걸러내기 — Where 는 조건이 True 인 요소만 남깁니다.

Dim numbers As List(Of Integer) = New List(Of Integer) From {5, 2, 8, 1, 9}
Dim evenNumbers = numbers.Where(Function(x) x Mod 2 = 0).ToList()

결과는 2, 8 입니다. 끝에 .ToList() 를 붙이지 않으면 결과가 그 자리에서 계산되지 않고, 나중에 실제로 꺼내 쓸 때 계산됩니다. 그 사이에 원본 리스트가 바뀌면 결과도 따라 바뀌니, 결과를 고정해 두고 싶으면 ToList() 로 확정해 두는 게 안전합니다.

다른 값으로 바꾸기 — Select 는 각 요소를 다른 값으로 변환한 결과를 돌려줍니다.

Dim names As List(Of String) = New List(Of String) From {"Alice", "Bob", "Charlie"}
Dim nameLengths = names.Select(Function(name) name.Length).ToList()

이름 리스트가 글자 수 리스트(5, 3, 7)로 바뀝니다. DB 에서 읽어 온 객체 목록에서 콤보박스에 넣을 이름만 뽑는 식으로 정말 자주 씁니다.

누적 계산 — Aggregate 는 앞에서부터 값을 하나씩 접어 가며 누적합니다. acc 가 지금까지의 누적값, x 가 이번 요소입니다.

Dim numbers As List(Of Integer) = New List(Of Integer) From {5, 2, 8, 1, 9}
Dim sum = numbers.Aggregate(Function(acc, x) acc + x)

결과는 25 입니다. 단순 합계라면 numbers.Sum() 이 더 읽기 쉽고, Aggregate 는 문자열을 특정 규칙으로 이어 붙이는 것처럼 기본 함수로 안 되는 누적에 쓰는 편이 맞습니다.

최대값 — 원문에는 이렇게 되어 있었습니다.

Dim numbers As List(Of Integer) = New List(Of Integer) From {5, 2, 8, 1, 9}
Dim max = numbers.Max(Function(x, y) If(x > y, x, y))

그런데 Max 가 받는 람다는 요소 하나를 받아 비교할 값을 돌려주는 매개변수 하나짜리라서, 두 개를 받는 이 람다는 컴파일되지 않습니다. 두 값을 비교하며 큰 쪽을 남기는 방식으로 쓰고 싶다면 Aggregate 로 바꾸고, 그냥 최대값이 필요하면 Max() 를 쓰면 됩니다.

Dim max1 = numbers.Aggregate(Function(x, y) If(x > y, x, y))   ' 9
Dim max2 = numbers.Max()                                       ' 9
Dim longest = names.Max(Function(name) name.Length)            ' 7 (선택자 람다는 매개변수 하나)

빛나는 원 안에 리본 모양이 떠 있는 코드 배경 일러스트

여러 줄 람다와 이벤트 핸들러

처리 내용이 한 줄로 안 끝나면 여러 줄 람다를 씁니다. 이때는 End Function/End Sub 로 닫고, Function 이면 Return 이 필요합니다.

Dim grade = Function(score As Integer) As String
                If score >= 90 Then Return "A"
                If score >= 80 Then Return "B"
                Return "C"
            End Function

AddHandler Button1.Click, Sub(s, e)
                              MessageBox.Show("클릭됨")
                          End Sub

폼에 동적으로 만든 버튼에 클릭 처리를 붙일 때 이렇게 AddHandler 와 Sub 람다를 같이 쓰면, 이벤트 처리기 메서드를 따로 만들지 않아도 됩니다. 다만 람다로 붙인 핸들러는 나중에 RemoveHandler 로 떼어 내기가 어렵습니다. 같은 람다를 다시 써도 다른 대리자로 취급되기 때문이죠. 떼어 낼 일이 있다면 람다를 변수에 담아 두고 그 변수로 붙이고 떼야 합니다.

주황색 고리와 반달 모양이 겹친 코드 배경 일러스트

어디까지 람다로 쓸까

짧을 때 람다는 코드를 확실히 줄여 줍니다. 문제는 길어질 때입니다. 람다 안에 람다가 들어가고 조건이 몇 겹씩 쌓이면 읽는 사람이 한 줄을 몇 번씩 다시 봐야 하고, 디버깅할 때 중단점을 걸거나 호출 스택을 따라가기도 불편합니다. 저는 람다 본문이 서너 줄을 넘어가거나 같은 람다를 두 군데 이상에서 쓰게 되면 이름 있는 메서드로 빼고, 그 메서드를 AddressOf 로 넘기는 쪽으로 정리합니다. numbers.Where(AddressOf IsValid) 처럼 써도 람다와 똑같이 동작합니다.

루프 안에서 람다를 만들 때 바깥 변수를 붙잡는 동작도 한 번은 짚어 둘 만합니다. 람다는 변수의 "값"이 아니라 "변수 자체"를 기억하기 때문에, 나중에 람다가 실행될 때 그 변수의 그 시점 값이 쓰입니다. 람다를 리스트에 모아 두었다가 나중에 실행하는 코드라면, 루프 안에서 지역 변수에 한 번 복사해 두고 그 변수를 쓰는 습관을 들이면 헷갈릴 일이 줄어듭니다.

주황색 앵커 모양 고리가 빛나는 코드 배경 일러스트