애들센스


VBA에서 람다함수 만들기 오피스/VBA/Office.JS

블로그 단골손님, 우왕님의 천재적인 아이디어에 힘입어 VBA에서 람다함수를 만들어 보았다. 진짜 람다함수는 아니고, 엑셀의 이름기능과 VBA의 EVALUATE함수를 이용한 것이다.

이번에 만드는 람다함수는 간단하다. 변수X와 변수Y 사이의 난수값을 생성하는 RANDBETWEEN()함수를 이용할 것이다.
먼저 그림과 같이 엑셀에서 미리 이름을 만들어 둔다(코드로 만들수도 있지만)

LAMBDA_RANDXY는 람다함수의 이름이라고 생각하고 수식 =RANDBETWEEN(ParamX, ParamY)은 람다함수의 본체이다.
그리고 ParamX와 ParamY는 변수X, 변수Y이다. ParamX와 ParamY의 값을 람다함수 LAMBDA_RANDXY가 받아서 RANDBETWEEN(ParamX, ParamY)에 전달한다고 생각하면 된다.

다음은 이 세 개의 이름을 가지고 람다함수 비스무리한 것을 구현한 것이다(내 눈을 바라봐~ 넌 람다함수가 보이고~)
1    Dim nmLambda As Name
2 Dim paramX As Name
3 Dim paramY As Name

4 Set nmLambda = ThisWorkbook.Names("LAMBDA_RANDXY")
5 Set paramX = ThisWorkbook.Names("ParamX")
6 Set paramY = ThisWorkbook.Names("ParamY")

7 paramX.Value = 12
8 paramY.Value = 20

9 Debug.Print Evaluate(nmLambda.Value)
1~3번 행은 워크북에 저장된 이름을 저장할 개체변수이다.
4~6번 행은 선언한 개체변수에 이름을 할당한다.
7~8번 행에서 변수X, 변수Y에 해당하는 ParamX와 ParamY의 값을 변경하다.
9번 행에서 Evaluate()함수를 이용하여 LAMBDA_RANDXY의 값, =RANDBETWEEN(ParamX, ParamY)를 계산한다.

이번에는 스칼라 값이 아니라 배열을 다루는 람다함수를 만들어 본다. 함수를 만들기에 앞서 먼저 풀어야할 썰이 하나 있다.
엑셀의 배열은 컬럼은 컴마(,)로 구분하고 , 행은 세미콜론(;)으로 구분하여 만든다. 가령 아래와 같은 3x3 행렬이 있다고 하자.

엑셀은 내부적으로 이것을 {1,4,7;2,5,8;3,6,9} 로 처리한다.
그래서 VBA의 배열을 람다함수에 던져주려면 위와 같은 형식의 문자열을 만들어야 한다.

다음은 SUMPRODUCT()함수에 배열을 던져주고 계산결과를 받아오는 람다함수이다.
01    Dim nmLambda As Name
02 Dim arrX As Name
03 Dim arrY As Name
04 Dim x(4), y(4)

05 Set nmLambda = ThisWorkbook.Names("LAMBDA_SUMP")
06 Set arrX = ThisWorkbook.Names("arrX")
07 Set arrY = ThisWorkbook.Names("arrY")

08 x(0) = 1990
09 x(1) = 29290
10 x(2) = 36990
11 x(3) = 9790
12 x(4) = 8590

13 y(0) = 12490
14 y(1) = 22990
15 y(2) = 8990
16 y(3) = 8190
17 y(4) = 10990

18 arrX.Value = "={" & Join(x, ";") & "}"
19 arrY.Value = "={" & Join(y, ";") & "}"

20 Debug.Print arrX.Value
21 Debug.Print arrY.Value

22 Debug.Print Evaluate(nmLambda.Value)
01~04행은 변수를 선언한 부분이다. 04 행에 5칸짜리 배열이 선언되었다.

05~07 행은 저장한 이름을 개체변수에 저장하는 것이다.
08~ 17 행은 배열에 값을 넣는 숙달된 조교의 노가다이다. 실무에서 이런 일을 하지 않지만...
18~19 행은 Join함수를 이용하여 배열의 각 원소를 세미콜론(;)으로 구분된 문자열로 만드는 것이다. 엑셀의 행렬 만드는 규칙을 따르는 것이다.
20~21 행은 입력한 값을 한번 찍어 본다.
22 행에서 드디어 저장한 배열을 가지고 SUMPRODUCT를 계산한다. 그러나 지난 번 포스팅(VBA 배열에 앙심을 품은 자)에서 언급한 바와 같이 'LAMBDA_SUMP'는 엑셀에서 사용가능한 이름개체이므로 다음과 같이

Debug.Print [LAMBDA_SUMP]

로 표현해도 된다. 뭔가 VBA같지 않은 이상한 결과이다.

이걸 잘 활용하면 코드를 깔끔하게 - 나만 고칠 수 있어서 함부로 날 못짜르는 - 만들 수 있을 것 같다.

결론은 '우왕님, 땡큐베리감사~'

(추가)
람다함수는 임시로 만드는 간단한 함수의 형태이므로 좀 더 완벽을 기하기 위해 혹시 같은 이름이 있다면 닥치고(On Error Resume Next) 지우고(Names(~~~).Delete), 만들어(Names.Add) 계산한 후([LAMBDA_SUMP]), 다시 증거를 인멸(Names(~~~).Delete)하는 완전범죄를 해본다. 변수도 전혀 사용하지 않는다
    On Error Resume Next
With ThisWorkbook
.Names("LAMBDA_SUMP").Delete
.Names("arrX").Delete
.Names("arrY").Delete

.Names.Add "LAMBDA_SUMP", "=SUMPRODUCT(arrX,arrY)"
.Names.Add "arrX", Sheet2.Range("J5:J7")
.Names.Add "arrY", Sheet2.Range("K5:K7")

Debug.Print [LAMBDA_SUMP]

.Names("LAMBDA_SUMP").Delete
.Names("arrX").Delete
.Names("arrY").Delete
End With
물론 이것이 파이썬의 그것에 비해 부족하다. filter(), map(), reduce()와 같은 함수에 사용되는 파이썬을 따라가기는 역부족이다. 그러나 VBA에서도 이를 함수로 만들어 볼 수 있다(이건 준비중...)

덧글

  • 우왕 2019/12/03 11:16 # 삭제 답글

    저야말로...여기서 너무 잘 배우고 있습니다 ㅠㅠ
  • 타임버드 2019/12/03 11:19 #

    사실 개인적인 자료보관목적으로 시작한 거라 누구에게 도움이 되리라 예상못했습니다.
  • 우왕 2020/01/24 19:34 # 삭제 답글

    저도 한번 만들어 보았습니다
    https://blog.naver.com/nmh1999/221782209891
댓글 입력 영역