루아 테이블(Table) 2
목차
이전 학습에 이어서 루아 테이블에 대하여 알아보겠습니다.
테이블 생성자
테이블 생성자는 테이블의 생성과 초기화를 합니다.
아래의 days 는 일~토까지 초기화를 합니다. 이때 자동으로 인덱스가 1부터 7까지 생성되는 부분에 주의합니다. 다른 정적인(Static) 언어의 배열은 0부터 시작하니까 혼동이 올 수 있습니다.
print("[Hello Lua Lesson!]\n") days = {"Sun", "Mon", "Tues", "Wes", "Thu", "Fri", "Sat"} for i=1,7 do print(i .. " " .. days[i]) end
[Hello Lua Lesson!] 1 Sun 2 Mon 3 Tues 4 Wes 5 Thu 6 Fri 7 Sat
인덱스를 지정할 수 있는 문법도 당연히 있습니다. 속도적으로는 위의 숫자인덱스의 자동생성이 더 빠릅니다.
print("--------- start here ----------\n") myTable = { a = 7, b = 9, c = 5} print("myTable[\"a\"]: " .. myTable["a"]) print("myTable[\"b\"]: " .. myTable["b"]) print("myTable[\"c\"]: " .. myTable["c"]) print("\n--------- another way -----------\n") print("myTable.a: " .. myTable.a) print("myTable.b: " .. myTable.b) print("myTable.c: " .. myTable.c)
--------- start here ---------- myTable["a"]: 7 myTable["b"]: 9 myTable["c"]: 5 --------- another way ----------- myTable.a: 7 myTable.b: 9 myTable.c: 5
C의 구조체나 객체의 멤버변수를 사용할 때 쓰는 도트연산기호와 같아서 객체처럼 보이죠.
table 에 대한 자유도는 루아언어의 특징입니다만, 맘먹고 코드를 쓰면 아래처럼 마구 꼬아놓을 수 있습니다. 이런 것을 할 필요가 있을까 의문을 품어봅니다.
뭐 당연히 좋지 않은 방식입니다. 규칙과 통일성을 갖는 초기화를 할 필요가 있습니다.
w = {x = 22, y = 33} x = {math.sin(0)} w[1] = "new field" x.f = w print(w["x"]) print(w["y"]) print(w[1]) print(w) print(x.f[1]) print(x.f["x"]) print(x.f["y"])
22 33 new field table: 0000000000199c80 new field 22 33
규칙보다 중요한 것은 어떤 데이터를 표현하려고 하는지 생각해볼 필요가 있습니다. 그리고 어떻게 표현하는게 가장 깔끔할지 군더더기가 없을지도요.
myRectangle = { color="black", thickness=1, transparent=0.7, {x=0, y=0}, {x=5, y=0}, {x=5, y=5}, {x=0, y=5} } print(myRectangle["color"]) print(myRectangle["thickness"]) print(myRectangle["transparent"]) print(myRectangle[1].x) print(myRectangle[1].y) print(myRectangle[2].x) print(myRectangle[2].y) print(myRectangle[3].x) print(myRectangle[3].y) print(myRectangle[4].x) print(myRectangle[4].y)
black 1 0.7 0 0 5 0 5 5 0 5
사각형이라는 이미지를 떠올리고 0,0 에서 5,5 까지 좌표를 표현했습니다. 테이블의 인덱스는 저렇게 사용할 수 있습니다. C언어 계열 문법을 오래 사용했기 때문에 좀 생소한 느낌이 드는데요. 연습을 해서 익숙해져야 할 듯 합니다.
아래의 코드를 보면 어떤 표준화에 대한 아이디어를 얻을 수 있습니다. 인덱스를 명확하게 초기화 시켜준다면 코드의 가독성이 좋아질 것 입니다. 유연성이 높은 만큼 프로그래머의 책임이 높아지는 것 입니다.
tableArray = { x=7, y=4 } print("tableArray[\"x\"]: " .. tableArray["x"]) print("tableArray[\"y\"]: " .. tableArray["y"]) tableArray1 = { ["x"]=77, ["y"]=43 } print("tableArray1[\"x\"]: " .. tableArray1["x"]) print("tableArray1[\"y\"]: " .. tableArray1["y"]) tableArray2 = {"apple", "kiwi", "orange"} print("-----------------------") for i=1,3 do print(tableArray2[i]) end print("-----------------------") tableArray3 = { [1]="apple", [2]="kiwi", [3]="orange"} for i=1,3 do print(tableArray3[i]) end
tableArray["x"]: 7 tableArray["y"]: 4 tableArray1["x"]: 77 tableArray1["y"]: 43 ----------------------- apple kiwi orange ----------------------- apple kiwi orange
배열 길이 연산자 #
배열의 길이를 가져오는 연산자 #가 있습니다.
이 연산자의 한계는 시퀀스에만 통한다는 것입니다. 시퀀스는 중간에 nil 이 없이 연속으로 요소가 채워져 있는 테이블을 의미합니다. nil 이 요소 사이에 한개가 있는 경우는 파악이 가능한데 두개 이상 nil 이 들어가면 끓어져 버립니다. (sequence with holes)
a = {"abc", "bbc", nil, "ccd"} a[#a + 1] = 111 for i=1, #a do print(a[i]) end
abc bbc nil ccd 111
위에서는 정상 출력하는 것들이 아래 코드에서는…
a = {"abc", "bbc", nil, nil, "ccd"} a[#a + 1] = 111 for i=1, #a do print(a[i]) end
abc bbc
끓어져 버립니다. 이는 # 연산자의 작동방식을 모호하게 합니다.
아래처럼 인덱스를 명시한 경우는 nil 이 한개만 있어도 끝납니다.
a = {} a[1] = 1 a[2] = nil a[3] = 1 for i=1, #a do print(a[i]) end [실행값] 1
그렇습니다. 정확한 원칙을 파악하기 어렵기 때문에 이 부분이 오랫동안 lua 의 발암 유발자로 남아있습니다.
호베르트의 Programming in Lua 에서는 이 부분을 잡고 한참 소모적인 설명을 하다가 결론 없는 급마무리를 하기 때문에 읽는 사람들 더 딥빡치게 합니다.
결론은 뭐냐? 아, 그러니까 조심하라구! 그래도 Lua 에서 대부분의 테이블은 Sequence 를 유지하기 때문에 괜찮아~ 라고 대강 끝을 내버립니다.
음… 루아라는 프로그래밍 언어가 왜 개발된거지 생각을 해보고 그냥 넘어가기로 합니다. 이는 유닉스 같은 OS 를 개발하기 위한 언어가 아닙니다. 게임스크립팅 같은 좀 창의적인 적용을 목적으로한 언어이므로 헛점이 있을 수 있습니다.
게임에서 수많은 버그를 볼 수 있는 것은 아무리 훌륭한 프로그래머도 사용자의 모든 행동을 예측하는 것은 불가능하기 때문이죠. 사용자는 사람입니다. 사람은 보통 예측가능하지만 PC방에서 마우스만 잡으면 폭주하는 사람들을 보면… 적어도 게임에서는 예측이 힘들겠구나… 라는 사실은 알 수 있습니다.
테이블 횡단 (Traversal)
테이블의 횡단이란 C언어로 배열을 흝는 for 문과 비슷합니다.
python 의 enumeration 처럼 인덱스-값을 만드는 방법은 pairs 함수를 사용합니다.
table 이 유연한 만큼 횡단 방법도 복잡해지죠? 기존의 규칙에 익숙한 사람들이라면 루아의 새로운 규칙에 적응하기가 쉽지는 않습니다. 그러나 하나씩 뜯어보면 별로 어려운 것들이 아니라 trick 같은 내용이므로 너무 빠져있을 필요는 없습니다.
어느정도 규칙을 확인하고 넘어가는 것으로 충분합니다.
table1 = {10, x = 12, print, k = "hi"} for k, v in pairs(table1) do print(k, v) end print("----------------------------") for i=1 ,#table1 do print(i, table1[i]) end table2 = {10, print, "hi", 23} print("----------------------------") for k, v in ipairs(table2) do print(k, v) end print("----------------------------") for i=1 ,#table2 do print(i, table2[i]) end
1 10 2 function: 0000000065b9cd60 k hi x 12 ---------------------------- 1 10 2 function: 0000000065b9cd60 ---------------------------- 1 10 2 function: 0000000065b9cd60 3 hi 4 23 ---------------------------- 1 10 2 function: 0000000065b9cd60 3 hi 4 23
요약
이전 학습에 이어서 루아 테이블에 대하여 알아봤습니다. 루아의 테이블 자료구조는 가장 tricky 하면서 논란의 중심에 있으면서도 가장 유용하다고 이야기합니다.
기존의 메인 언어인 자바나 JS 등 언어를 사용해본 사람들에게는 또 하나의 사고전환이 될 수 있을 것 같습니다.
다소 난해한 루아의 테이블을 한번에 다 이해하기는 어려울 것입니다. 사용법을 하나씩 배워가는 것이 좋습니다.