프날 오토핫키 강좌
누르면 강좌 리스트가 나와요
프날 오토핫키 강좌

⚠ 이 강좌는 오토핫키 v1을 다룹니다

지금 보시는 강좌는 구버전 오토핫키(v1.1)를 다루고 있습니다. 따라서 본 강좌의 내용은 현재 최신 오토핫키 버전 (v2.0)과 호환되지 않습니다. 구버전의 정보가 필요한 것이 아니라면, 가능한 한 새로운 사이트에 작성한 v2 강좌(https://ahkv2.pnal.dev)를 봐주시길 바랍니다.

[프날 오토핫키] 배열 #6: 다차원 배열

지금까지 내용은 말만 어렵고 실제로 스크립트 작성을 해보면 은근 쉬웠을 것입니다. 그도 그럴게, 아래와 같은 두 가지 형태의 배열이 거의 유사할 뿐더러 사용법도 쉽기 때문입니다.

simpleArr := [1, 3, 5, 7, 9]
associativeArr := {"key1": 2, "key2": 4, "keyn": 6}
MsgBox, % simpleArr[4] "`n" associativeArr["key1"] ", " associativeArr.key2

다차원 배열 1.ahk
0.00MB

** 파일명만 이렇고 다차원 배열은 아닙니다 **

실행 결과

 

표현식의 개념만 정확히 안다면 그렇게 어려운 부분이 없었지요. 그러나 이번 강은 다소 어려울 수 있습니다. 많은 컴퓨터공학과 신입생들의 의욕을 좌절시켰던 '다차원 배열'인데, 여러분들 중 사용하는 분이 몇이나 있겠냐만은 일단 소개드릴려고 합니다.

 


 다차원 배열이란? 

 

다차원 배열은 1차원배열이 아닌 배열을 뜻합니다(2차원, 3차원 등...). 당연한 이야기죠? 아니, 배열에 차원이라니, 갑자기 무슨 수학적인 이야기냐 싶습니다. 그렇지만 배열에는 차원이 있습니다. 1차원은 선, 2차원은 면, 3차원은 공간 등... 다들 아는 내용이죠? 학창시절 수학을 많이 공부하신 분은 이 '차원'개념이 다항식에서 '차수(혹은 거듭제곱)'의 개념과 밀접히 연관되어있음을 아실겁니다.

 

그런데 일단 수학을 좀 배제하고 보자고요. 물론 프로그래밍에서 수학은 상당히 중요하지만, 적어도 배열 강좌와 같은 기초적인 부분에서까지 끌어올 필욘 없습니다. 그림으로 배우는게 제일 직관적일 것 같군요.

 

단순 배열로 생각해보죠. 배열의 요소를 쭉 나열해보면, 한 개의 기준(여기선 인덱스가 되겠네요)으로 표현할 수 있습니다. 1, 2, 3..., 99, ... 3000 등 숫자 하나로 배열의 어느 부분을 가리키는지 정확히 알 수 있죠. 이렇게 배열의 위치를 표기하는데 단 하나의 기준정보만 필요한 배열을 '일차원 배열'이라고 합니다.

 

그렇다면 두 개의 기준정보로 배열 요소의 위치를 표현하는 배열은 '이차원 배열'이라고 하겠죠.

이렇게 배열이 면적의 형태로 존재한다면 이차원 배열이라고 부를 수 있습니다. 배열의 한 요소(사진에서 C2)를 찾기 위해 두 가지 기준 정보(C, 2)를 사용하죠. 여러분이 영화관 좌석을 예매할 때, 엑셀 프로그램을 사용할 때 등 많은 경우에 이렇게 두가지 기준 정보를 사용합니다. 수학적으로 이런 '기준 정보'는 '축'이라고 하고요. 

 

눈여겨 볼 점은, 이러한 이차원 배열의 형태를 자세히 살펴 보면 '일차원 배열이 아래로 쌓여있는' 모습임을 알 수 있습니다. 즉, 위 그림에서 A1~E1 부분만 떼서 보면 영락없는 일차원 배열인거죠. 그래서 이차원 배열을 "배열의 배열이다"라고 합니다. 여러개의 배열이 상하로 배열되어있으니까요.

이차원 배열은 일차원 배열이 쌓여있는 모습입니다.

 

삼차원 배열도 있을까요? 있습니다. 세 가지 축을 사용하면 되네요. 다른 말로 표현하면, 이차원 배열을 여러겹 쌓으면 됩니다.

세상의 한계로 인해 4차원 이상의 차원은 그림으로 명확하게 보여드릴 순 없지만, 아무튼 수학 및 프로그래밍에선 만들 수 있습니다. 아무튼 배열은 이런식으로 다차원화 시킬 수 있습니다.

 

그런데 일반적으로 이차원 배열까지가 자주 사용되고, 그 이상의 배열은 종종 사용되나 가독성의 문제 등으로 별개의 배열로 분리하는 경우가 꽤 있어서 이번 강좌에는 이차원 배열을 기준으로 주로 설명드리도록 하겠습니다.

 


 이차원 배열 

이차원 배열의 시각적 표현

위의 그림을 다시 가져와야겠네요. 아래처럼 두 개의 축으로 이루어진 배열을 이차원 배열이라고 합니다. "배열의 배열" 이라고도 한다고 설명 드렸습니다.

왜 이런 배열을 써야하는 상황이 있을까요? 그건 프로그래머인 여러분들이 생각할 문제입니다. 문제 해결 과정에서 이를 사용할 때가 올 것입니다. 뭐, 간단하게 생각해서 성적 관리 프로그램을 만들어본다고 생각해봅시다. 위 그림에서 1, 2, 3을 학생 이름으로, A~E를 각 과목으로 치환해봅시다.

 

즉, 1번 학생의 국어, 영어, 수학, 탐구, 한국사 점수를 A1, B1, C1, D1, E1 칸에 담아보자는 것입니다. 성적 관리가 한결 쉽겠죠? 2번 학생이면 A2, B2, ... , E2의 값을 읽어오면 되니까요.

 

또, 배열 강좌 서론에서 예를 들었던 학생 관리 프로그램이 기억나시나요? 독서실에 등록된 학생의 이름, 입실 시간, 퇴실 시간 등을 기록하는 프로그램이었습니다. 1번 학생의 {이름, 입실 시간, 퇴실 시간}을 1행에, 2번 학생의 {이름, 입실시간, 퇴실 시간}을 2행에 넣는 식으로 프로그래밍하면 깔끔할 것 같습니다. 특히, 이름/입퇴실 시간 등은 단순한 숫자 인덱스가 아닌 의미가 있는 단어(name, enterTime, exitTime등)로 변수명을 지어주면 좋으므로 '연관 배열'로 만들면 좋겠군요! n번 학생의 이름은 n행의 name열에 들어있겠네요.

 

아무튼, 이차원 배열은 "배열의 배열"이라고 했습니다. 그러면 배열 요소로 배열을 넣어주면 된단 뜻이겠네요! 한번 해보겠습니다.

dim := [["가", "나"], ["다", "라"], ["마", "바"]]

 

이 배열은 아래와 같은 구성을 가지고 있을 것입니다.

[가 , 나] 배열, [다, 라] 배열, [마, 바] 배열이 3단으로 쌓여있는 모습입니다(이런 배열을 3x2 배열, 혹은 3행2열 배열이라고 부르곤 합니다.)

 

 

이차원 배열의 요소에 접근하는 방법 : 사용

이런 이차원 배열에 접근하기 위해서는 아래와 같이 첨자 연산자([]) 안에 행과 열을 표시해줍니다.

dim[3][1]
dim[3, 1]

두 방법 모두 상관 없습니다. 차이점에 대해선 아래에서 설명드리겠습니다.

위와 같이 행과 열을 각각의 첨자연산자에 넣어서 붙여 표기해줍니다. 

dim := [["가", "나"], ["다", "라"], ["마", "바"]]
MsgBox, % dim[3][1]

다차원 배열 2.ahk
0.00MB

이렇게 접근할 때 먼저 쓴 숫자가 '행', 그러니까 세로축입니다. 나중에 쓴 숫자가 '열', 그러니까 가로축입니다. 세로축은 3까지, 가로축은 2까지 있네요. 

오토핫키 공식 래퍼런스에선 '행(row)'을 x, '열(col)'을 y로 적는 바람에 table[x][y]로 표기하고 있긴 하나, 수학에서의 좌표는 가로축이 x임에 반해 다소 오해의 소지가 있는 변수명을 사용했네요. 수학적으로 표기하면 table[y][x] 꼴이 맞습니다.

이해를 위해 같은 사진을 또 가져왔습니다.

즉, 위 예시에서 dim[3][1]은 세로로 세 번째, 가로로 첫 번째에 있는 요소를 가져옵니다. "마" 를 출력하겠네요.

 

 

이차원 배열의 요소에 접근하는 방법 : 할당

이번엔 할당입니다. 일차원 단순 배열은 아래와 같이 선언 후 할당이 되었죠

arr := []
arr[2] := "나"

 

이차원 배열도 아래처럼 하면 된다고 생각하기 쉽습니다.

dim := [] ;뭐 혹은.... dim := [][]나 dim := [[]]라고 생각할수도 있고요.
dim[3][2] := "나"

 

위 스크립트에서 MsgBox를 사용해서 dim[3][2]를 출력하면 값이 나오나요? 아마 빈 값이 나올 것입니다. 이에 관해선 복잡한 이유가 있습니다. 복잡하긴 하지만 읽어보세요.

 

dim[3][2]라는 구문을 분석해보죠. 첨자 연산자(대괄호)는 그 앞의 적혀있는 배열의 인덱스에 접근하는데 사용됩니다. 즉, dim[3] 이라는 배열의 [2]번째 요소에 접근하라는 구문입니다. 그런데 dim[3]이 빈 값이기 때문에, dim[3][2]는 빈 값의 두 번째 요소에 접근하는 엉뚱한 구문이 되어버립니다. 이렇듯 "배열명[행][열]"로 접근하면 행이 비어있을 경우엔 열 접근이 되지 않습니다. 당연한 이치죠.

 

그래서 오토핫키는 위에서 잠깐 등장했듯 배열 요소에 접근하는 다른 방법을 제공합니다. 바로 "배열명[행, 열]"로 쓰는 방법이죠. 이렇게 쓰면 첨자 연산자가 1개이기 때문에, 배열명만 가지고 (행이 없어도) 접근이 가능합니다. 이 경우엔 그 행이 없으면 행을 만들어서 접근합니다! 따라서, 세 번째 행이 없는데 dim[3][1]에 "나"라는 문자열을 할당하면, 세 번째 행을 만들어서 그 행의 첫번째 요소에 값을 할당하게 되는 것이지요.

 

아무튼 결론만 말씀드리면, 행이 없을 때 새로 할당을 할 땐 아래와 같이 써야하고,

dim[행, 열] := 값

 

그리고 행이 이미 있는 경우엔 아래와 같은 방법 둘 다 사용할 수 있습니다.

dim[행][열] := 값
dim[행, 열] := 값

 

타언어에서 정석적인 방법이 dim[행][열]이고 이것이 자연스러운 방법입니다. 따라서 행을 새로 만들어서 할당할 때만 dim[행, 열]을 쓰면 되겠습니다.

 

그럼 할당을 해볼까요?

dim := []
dim[2][2] := "나" ;요소 할당 안됨 - 행이 없으면 안됨.
MsgBox, % dim[2][2]
dim[2,2] := "나" ;요소 할당 됨 - 행이 없으면 행을 만든다!
MsgBox, % dim[2][2]

다차원 배열 3.ahk
0.00MB
위 예시의 배열 표입니다.


 배열에 배열 넣기 

이차원 배열은 배열의 배열이라고 했습니다. 그렇다면, 이미 만들어진 일차원 배열의 요소로 또 다른 일차원 배열을 할당하면, 이차원 배열로 쓸 수 있겠지요. 즉, 아래와 같이 쓸 수 있습니다.

dim := [1, 2, 3]
dim[1] := ["가", "나"]
Msgbox, % dim[1][2]

다차원 배열 4.ahk
0.00MB

dim은 원래 일차원 배열입니다. 그러나 dim의 첫번째 요소로 또다른 일차원 배열을 넣어주었네요.

이렇게 된다면 dim[1][1]은 "가", dim[1][2]는 "나"를 가지는 이차원 배열입니다. 그림으로 표현하면 아래와 같은 상태입니다.

일부는 이차원 배열이고, 일부는 일차원 배열인 셈이죠. 표로 표현하면 이렇게 부자연스러운 모양이 되지만, 반대로 '폴더'처럼 생각해보면, 파일 두 개가 들어있는 폴더 한개, 일반 파일 두 개가 있다고 생각하시면 됩니다.

폴더에는 "가", "나" 요소 두 개가 있습니다.

이런 폴더 개념으로 다차원 배열을 이해하시는 분도 계십니다. 어떻게 이해할지 선택은 본인의 몫입니다.

 

여러분이 아셔야 할 것은, 배열엔 배열을 넣을 수 있고, 이차원 배열이 그런 형태라는 것입니다. 따라서, 함수에서 일차원 배열(연관 배열이든 단순 배열이든)을 return해서 그 값을 다른 배열에 담아 쓴다던가 하는 형태의 프로그램을 개발할 수 있죠.


 이차원 연관 배열 

 

연관 배열도 다차원으로 만들 수 있는데, 하나만 짚습니다. 첨자 연산자([])로 다차원 접근을 할 때, 그 안에 인덱스 대신 Key를 넣어야하는 연관 배열의 특성상 가독성이 많이 떨어집니다.

 

그래서 연관배열은 아래와 같이 멤버 접근 연산자(.)를 사용하여 표현하는 편이 일반적입니다.

 

다차원 배열 5.ahk
0.00MB

두 번째 출력 방식이 제일 가독성이 좋아서 그런지 자주 사용되곤 합니다. 행은 첨자 연산자로 접근하고, 열은 멤버 접근 연산자를 이용하는 것이죠. 다시한번 말씀드리지만, 오토핫키의 배열은 객체 기반이기 때문에 이렇게 멤버 접근 연산자로 각 요소에 접근할 수 있습니다!

 


갑자기 배운게 많아진 느낌이죠? 그러나 다차원 배열 개념은 분명 알아두면 도움이 됩니다. 배열 안에 배열을 넣을수 있음으로써 여러 이점이 생기기 때문입니다. 복잡한 자료 구조도 이차원 배열 하나로 표현하기도 하죠. 

삼차원 이상의 배열을 사용하는 경우는 드물기 때문에 생략했지만, 이차원 배열까지의 사용에 익숙해지면 그보다 고차원의 배열도 쉽게 사용할 수 있습니다. 다만 너무 차원의 수가 많아지면 별개의 배열로 분리시켜서 좋을때가 많습니다.

 

이해가 안되더라도 직접 해보면 감이 잡힐 것입니다. 예제는 직접 타이핑 하는 편을 추천드립니다.

이차원 배열이라고 해봤자 사실 가로 세로만 헷갈리지 않으면 쓰는덴 지장이 없을 것입니다. 다만 쓰는데 지장 없겠다고 만족하면 안되겠죠. 다음 강에서 난이도 별로 배열 문제를 잔뜩 준비해보았습니다.

 

배열 강좌는 6편(배열 문제까지 별강으로 치면 7편)으로 마무리 짓도록 하겠습니다.

지금까지의 내용을 전부 익힌다면, 분명 프로그래밍의 질이 달라질 것입니다. 체계적으로 잘 작동하는 프로그램의 기쁨을 느껴볼 수 있습니다.