루아(코로나)의 테이블의 요소(field)는 숫자를 키(key)로 가지는 것들과 문자열을 키로 가지는 것들로 구분할 수 있다. 예를 들어서


          local tA = { 11, x=160, “hello"35, y=200, true}


위 테이블은 숫자키를 가지는 요소가 색으로(굵게) 표시된 것들이며 아래와 완전히 동일하다.


          local tA = {

                    [1]=11-- 숫자키

                    [“x”]=160,

                    [2]=“hello"-- 숫자키

                    [3]=35-- 숫자키

                    [“y”]=200,

                    [4]=true-- 숫자키

          }

즉, 숫자키를 가지는 요소는 네 개이고 문자열키를 가지는 요소는 두 개다. 특이한 것은 루아는 아무 키도 지정하지 않은 요소는 자동으로 숫자키가 1부터 시작해서 할당되며, 또한 사용자가 아무 숫자로 키를 지정할 수 있다는 것이다. 0으로 지정할 수도 있고 음수도 가능하며 심지어 실수도 가능하다.


          local tB = { [-10]=11, 21, 33, x=50, 44 }


  한 가지 오해하기 쉬운 것은 위의 예에서 두 번째 요소인 21의 숫자키가 무엇일까 하는 것이다. -9라고 생각하기 쉬운데 1이다. 앞에서도 언급했듯이 아무 키도 지정되지 않는 요소는 항상 숫자키 1로 부터 시작해서 1씩 증가시킨다.


          local tC = { [2]=11, 22, 33, 44 }


위의 예에서 11의 숫자키가 2로 지정되었는데 33도 2가 되서 숫자키가 겹치게 되므로 보통은 이렇게 정의하지 않을 것이다.


  주의할 점은 테이블의 크기를 구하는 #연산자를 사용할 때이다. #연산자는 자연수(즉, 양의 정수 1,2,3, ...) 숫자키를 가지는 요소만 고려한다는 점에 유의해야 한다. 따라서 앞의 예에서 #tA 값은 4이고 #tB 값은 3이다. 다른 예를 들어 보면


          print( #{ 11, 22, “abc", 44} ) -- 4가 찍힌다.

          print( #{ 11, 22, "abc", x=4} ) -- 3이 찍힌다.

          print( #{ [0]=11, 22, "abc", x=4} ) -- 2가 찍힌다.


  좀 더 정확히 얘기하면 루아에서 #의 정의는 '자연수키를 가지는 요소 중에서 [n]번 요소는 nil이 아니고 [n+1]요소가 nil일 때의 n값(들)' 이다. 테이블을 자연수키로만 얌전하게(...) 관리한 경우 #연산자로 그 크기를 정확하게 구할 수 있다.


  혼동의 여지가 있는 경우는 중간에 nil이 섞여 있을 때이다. 이 경우는 #연산자로 정확한 크기를 구할 수 없다.


          print( #{ 11, 22, nil ,44 } ) -- 4가 찍힌다.

          print( #{ 11, 22, nil, 44, 55, nil } ) -- 2가 찍힌다.


또한 for와 ipairs()함수를 조합하면 테이블의 자연수키 요소만을 고려하게 된다. 그런데 이 경우에도 nil이 나오면 거기서 멈춘다는 것에 주의해야 한다.


          local tE = {11, 22, x=33, 44, nil, 55}

          local cnt = 0

          for key, value in ipairs(tE) do

                    cnt = cnt + 1

                    print(key.." : ".. value) -- 1:11, 2:22, 3:44 까지만 찍힌다.

          end


위의 예에서 문자열키를 가지는 x=33는 건너뛰고, 44까지는 가는데 그 다음의 nil 때문에 55까지는 도달하지 못하게 된다. (이에 반해서 pairs() 함수는 nil을 제외한 숫자키와 문자열키를 가지는 요소 모두를 고려한다.)

  코로나로 코딩을 하다보면 다양한 객체들 (이미지, 타이머, 애니메이션 등등)을 테이블에 저장하여 배열로 관리하는 경우가 있을 것이다. 예를 들어서 다음과 같은 방식이다.


          local tTimer = {}

          local tSprite = {}

          ...

          tTimer[#tTimer+1] = timer.performWithDealy( 500, SomeFunc ) -- 테이블에 타이머레퍼런스 저장

          tSprite[#tSprite+1] = display.newSprite( someSheet, someSequence ) -- 테이블에 애니메이션 저장

          ...


이런 식으로 배열에 타이머나 애니메이션을 저장하면서 관리하다가 어떤 조건에 따라서 중간의 요소를 삭제하기도 할 것이다.


          ...

          timer.cancel( tTimer[some_Id] )

          tTimer[some_Id] = nil

          ...

          tSprite[ another_id ]:removeSelf()

          tSprite[ another_id ] = nil


이런 경우 위에서 언급한 내용을 고려해서 테이블의 크기를 구하거나 반복문으로 테이블에 저장된 요소들을 검사할 때 논리적인 오류가 발생하지 않도록 해야 한다. 테이블의 요소를 완전하게 제거하려면 table.remove() 함수를 사용해야 한다.


Posted by 살레시오
,