수색…


통사론

  • ipairs (numeric_table) - 숫자 인덱스 iterator가있는 Lua 테이블
  • 쌍 (input_table) - 일반 루아 표 반복자
  • 키, 값 = 다음 (input_table, input_key) - 루아 테이블 값 선택자
  • table.insert (input_table, [position], value) - 지정된 값을 입력 테이블에 삽입합니다.
  • removed_value = table.remove (input_table, [position]) - 위치에 의해 지정된 값을 마지막으로 제거하거나 제거합니다.

비고

테이블은 Lua에서 사용할 수있는 유일한 기본 제공 데이터 구조입니다. 이것은보기에 따라 단순하거나 혼란 스럽습니다.

루아 테이블은 키가 고유하고 키나 값이 nil 이 아닌 키 - 값 쌍의 모음입니다. 따라서 Lua 테이블은 사전, 해시 맵 또는 다른 언어의 연관 배열과 유사 할 수 있습니다. 스택, 큐, 세트,리스트, 그래프 등 많은 구조 패턴을 테이블과 함께 만들 수 있습니다. 마지막으로 테이블을 사용하여 루아에서 클래스 를 만들고 모듈 시스템을 만들 수 있습니다.

루아는 테이블 사용 방법에 대한 특별한 규칙을 시행하지 않습니다. 테이블에 포함 된 항목은 Lua 유형이 혼합되어있을 수 있습니다. 예를 들어, 하나의 테이블에는 문자열, 함수, 부울 값, 숫자 및 다른 테이블 이 포함될 수 있습니다.

연속적인 양의 정수 키가 1로 시작하는 Lua 테이블은 시퀀스를 가지고 있다고합니다. 양의 정수 키가있는 키 - 값 쌍은 시퀀스의 요소입니다. 다른 언어에서는 이것을 1 기반 배열이라고 부릅니다. 특정 표준 연산과 함수는 테이블의 시퀀스에서만 작동하고 일부는 시퀀스가없는 테이블에 적용될 때 비 결정적 동작을합니다.

테이블의 값을 nil 설정하면 테이블에서 값이 제거됩니다. 반복자는 더 이상 관련 키를 볼 수 없습니다. 시퀀스가있는 테이블을 코딩 할 때 시퀀스를 손상시키지 않는 것이 중요합니다. 마지막 요소 만 제거하거나 표준 table.remove 와 같은 함수를 사용하여 요소를 아래로 이동하여 간격을 닫습니다.

테이블 만들기

빈 테이블을 만드는 것은 다음과 같이 간단합니다.

local empty_table = {}

간단한 배열 형식으로 테이블을 만들 수도 있습니다.

local numeric_table = {
    "Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.

기본적으로 테이블 인덱싱은 1부터 시작한다는 점에 유의하십시오.

또한 연관 요소가있는 테이블을 만드는 것이 가능합니다.

local conf_table = {
    hostname = "localhost",
    port     = 22,
    flags    = "-Wall -Wextra"
    clients  = {                -- nested table
        "Eve", "Jim", "Peter"
    }
}

위의 사용법은 아래에있는 구문 설탕입니다. 이 인스턴스의 키는 유형 문자열입니다. 위의 구문은 테이블을 레코드로 표시하도록 추가되었습니다. 이 레코드 스타일 구문은 '기본 사용법'자습서에서 볼 수 있듯이 문자열 키가있는 테이블을 인덱싱하는 구문과 함께 사용됩니다.

설명 섹션에서 설명한 것처럼 가능한 모든 키에 대해 레코드 스타일 구문이 작동하지 않습니다. 또한 키는 모든 유형의 값일 수 있으며 앞의 예는 문자열과 순차적 숫자 만 다룹니다. 다른 경우에는 명시 적 구문을 사용해야합니다.

local unique_key = {}
local ops_table = {
    [unique_key] = "I'm unique!"
    ["^"]  = "power",
    [true] = true
}

반복 테이블

루아 표준 라이브러리는 테이블의 키와 값을 반복하는 pairs 함수를 제공합니다. pairs 반복 할 때 테이블의 키가 숫자 인 경우에도 순회에 대한 지정된 순서는 없습니다.

for key, value in pairs(input_table) do
    print(key, " -- ", value)
end

숫자 키를 사용하는 테이블의 경우 Lua는 ipairs 함수를 제공합니다. ipairs 함수는 첫 번째 nil 값이 발견 될 때까지 table[1] , table[2] 등에서 항상 반복됩니다.

for index, value in ipairs(numeric_table) do
    print(index, ". ", value)
end

ipairs() 를 사용하는 반복은 몇 가지 경우에 원하는대로 작동하지 ipairs() 경고하십시오.

  • input_table 에 "구멍"이 있습니다. 자세한 내용은 "배열로 사용되는 테이블의 갭 방지"섹션을 참조하십시오. 예를 들면 다음과 같습니다.

    table_with_holes = {[1] = "value_1", [3] = "value_3"}
    
  • 키가 모두 숫자가 아니 었습니다. 예 :

    mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}
    

물론 다음은 적절한 순서 인 테이블에도 적용됩니다.

for i = 1, #numeric_table do
    print(i, ". ", numeric_table[i])
end

숫자 표를 역순으로 반복하는 것은 쉽습니다.

for i = #numeric_table, 1, -1 do
    print(i, ". ", numeric_table[i])
end

테이블을 반복하는 마지막 방법은 generic for 루프 에서 next 선택기를 사용하는 것입니다. pairs 과 마찬가지로 순회를위한 지정된 순서가 없습니다. 합니다 ( pairs 방법을 사용하여 next 그래서 사용. 내부적으로 next 본질적으로보다 수동 버전 pairs . 참조 pairs 루아의 참조 설명서next 루아의 참조 설명서에서 자세한 내용을.)

for key, value in next, input_table do
    print(key, value)
end

기본 사용법

기본 테이블 사용에는 테이블 요소 액세스 및 지정, 테이블 내용 추가 및 테이블 내용 제거가 포함됩니다. 이 예제에서는 테이블을 만드는 방법을 알고 있다고 가정합니다.

요소에 접근하기

다음 표를 보면,

local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",
                       "Diarrhea", cure = "Pepto Bismol"}

인덱스 구문을 사용하여 테이블의 순차 부분을 인덱싱 할 수 있습니다. 인덱스 구문의 인수는 원하는 키 - 값 쌍의 키입니다. 생성 자습서에서 설명했듯이 대부분의 선언 구문은 키 - 값 쌍을 선언하기위한 구문 설탕입니다. 순차적으로 포함 된 요소는 example_table 의 처음 5 개 값과 마찬가지로 증가하는 정수 값을 키로 사용합니다. 레코드 구문은 필드 이름을 문자열로 사용합니다.

print(example_table[2])        --> Heartburn
print(example_table["cure"])   --> Pepto Bismol

문자열 키의 경우 테이블 작성시 문자열 키에 대한 레코드 스타일 구문과 유사한 구문 설탕이 있습니다. 다음 두 줄은 동일합니다.

print(example_table.cure)      --> Pepto Bismol
print(example_table["cure"])   --> Pepto Bismol

이전에 사용하지 않은 키를 사용하여 테이블에 액세스 할 수 있습니다. 다른 언어에서와 마찬가지로 오류는 아닙니다. 이렇게하면 기본값 nil 반환됩니다.

요소 할당

인덱스 구문을 사용하여 테이블에 할당하여 기존 테이블 요소를 수정할 수 있습니다. 또한 레코드 스타일 색인 구문을 사용하여 값을 설정할 수도 있습니다

example_table.cure = "Lots of water, the toilet, and time"
print(example_table.cure)    --> Lots of water, the toilet, and time

example_table[2] = "Constipation"
print(example_table[2])      --> Constipation

또한 할당을 사용하여 기존 테이블에 새 요소를 추가 할 수 있습니다.

example_table.copyright_holder = "Procter & Gamble"
example_table[100] = "Emergency source of water"

특별 참고 : 일부 문자열은 레코드 구문에서 지원되지 않습니다. 자세한 내용은 설명 섹션을 참조하십시오.

요소 제거하기

앞에서 설명한 것처럼 할당 된 값이없는 키의 기본값은 nil 입니다. 테이블에서 요소를 제거하는 것은 키 값을 다시 기본값으로 재설정하는 것만 큼 간단합니다.

example_table[100] = "Face Mask"

요소는 이제 설정되지 않은 요소와 구별 할 수 없습니다.

테이블 길이

테이블은 간단히 연관 배열 (주의 참조)이지만 연속적인 정수 키가 1부터 시작되면 테이블에 시퀀스 가 있다고합니다.

테이블의 시퀀스 부분의 길이를 찾는 것은 # 사용하여 수행됩니다 :

local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table)    --> 8

길이 조작을 사용하여 항목을 순서 테이블에 쉽게 추가 할 수 있습니다.

example_table[#example_table+1] = 'a'
print(#example_table)    --> 9

위의 예에서 #example_table 의 이전 값은 8 이고, 1 을 추가하면 시퀀스의 다음 유효한 정수 키인 9 를 얻을 수 있습니다. 따라서 example_table[9] = 'a' 입니다. 이것은 테이블의 길이에 상관없이 작동합니다.

특별 참고 : 연속적이지 않고 1에서 시작하는 정수 키를 사용하면 테이블을 스파 스 테이블 로 만드는 시퀀스가 ​​손상됩니다. 이 경우 길이 연산의 결과는 정의되지 않습니다. 비고 섹션을 참조하십시오.

표 라이브러리 함수를 사용하여 요소 추가 / 제거

테이블에 요소를 추가하는 또 다른 방법은 table.insert() 함수입니다. 삽입 기능은 순서 테이블에서만 작동합니다. 함수를 호출하는 두 가지 방법이 있습니다. 첫 번째 예제에서는 첫 번째 사용법을 보여줍니다. 여기서 하나는 요소를 삽입 할 색인 (두 번째 인수)을 지정합니다. 이렇게하면 주어진 인덱스의 모든 요소를 #table 까지 한 위치 위로 밀어 #table 수 있습니다. 두 번째 예는 인덱스가 지정되지 않고 주어진 값이 테이블의 끝 (index #table + 1 )에 추가되는 table.insert() 의 다른 사용법을 보여줍니다.

local t = {"a", "b", "d", "e"}
table.insert(t, 3, "c")        --> t = {"a", "b", "c", "d", "e"}

t = {"a", "b", "c", "d"}
table.insert(t, "e")           --> t = {"a", "b", "c", "d", "e"}

평행 table.insert() 제거하는 것은 요소 table.remove() . 비슷하게 그것은 주어진 위치에서 요소를 제거하기위한 것이고, 시퀀스의 끝에서 제거하기위한 것입니다. 시퀀스 중간에서 제거하면 다음 요소가 모두 하나의 인덱스 아래로 이동합니다.

local t = {"a", "b", "c", "d", "e"}
local r = table.remove(t, 3)       --> t = {"a", "b", "d", "e"}, r = "c"

t = {"a", "b", "c", "d", "e"}
r = table.remove(t)                --> t = {"a", "b", "c", "d"}, r = "e"

이 두 함수는 주어진 테이블을 변경합니다. table.insert()table.remove() 를 호출하는 두 번째 방법을 table.insert() 테이블에 스택 의미를 제공 할 수 있습니다. 이를 활용하여 아래 예제와 같은 코드를 작성할 수 있습니다.

function shuffle(t)
    for i = 0, #t-1 do
        table.insert(t, table.remove(t, math.random(#t-i)))
    end
end

아마도 비효율적으로 Fisher-Yates Shuffle을 구현합니다. 그것은 사용 table.insert() 동일한 테이블의 단부 상에 무작위로 추출 된 요소를 추가하고, table.remove() 랜덤 테이블 unshuffled 나머지 부분에서 요소를 추출.

배열로 사용되는 테이블의 틈새 예방

용어 정의

여기서 배열 이란 시퀀스로 사용되는 루아 테이블을 의미합니다. 예 :

-- Create a table to store the types of pets we like.
local pets = {"dogs", "cats", "birds"}

이 테이블은 시퀀스로 사용됩니다. 정수로 입력 된 항목 그룹입니다. 많은 언어들이 이것을 배열이라고 부릅니다. 그러나 엄밀히 말해 루아에는 배열과 같은 것이 없습니다. 테이블 중 일부는 배열과 유사하며 일부는 해시 형 (또는 사전 형, 원하는 경우)이고 일부는 혼합되어 있습니다.

우리의 pets 배열에 관한 중요한 점은 그것이 틈이 없다는 것입니다. 첫 번째 항목 인 pets[1] 은 "dogs"문자열이고 두 번째 항목 인 pets[2] 는 문자열 "cats"이고 마지막 항목 인 pets[3] 는 "birds"입니다. 루아의 표준 라이브러리와 루아를 위해 쓰여진 대부분의 모듈은 시퀀스의 첫 번째 인덱스로 1을 가정합니다. 따라서 gapless 배열에는 시퀀스의 숫자가 누락되지 않고 1..n 항목이 있습니다. 제한적인 경우 n = 1 이고 배열에는 항목이 하나만 있습니다.

루아는 빌트인 함수 ipairs 를 제공하여 그러한 테이블을 반복한다.

-- Iterate over our pet types.
for idx, pet in ipairs(pets) do
  print("Item at position " .. idx .. " is " .. pet .. ".")
end

이렇게하면 "위치 1의 항목은 개"입니다. "위치 2의 항목은 고양이입니다.", "위치 3의 항목은 새입니다."

그러나 우리가 다음과 같이하면 어떻게됩니까?

local pets = {"dogs", "cats", "birds"}
pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
  print("Item at position " .. idx .. " is " .. pet .. ".")
end

이 두 번째 예제와 같은 배열은 희소 배열입니다. 시퀀스에 간격이 있습니다. 이 배열은 다음과 같습니다.

{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1        2       3      4    5    6    7    8    9    10   11       12     

nil 값은 임의의 메모리를 차지하지 않습니다. 내부적으로 lua는 값 [1] = "dogs" , [2] = "cats" , [3] = "birtds"[12] = "goldfish"

즉각적인 질문에 답하기 위해, ipairsipairs 후 멈출 것입니다. pets[12] "금붕어" pets[12] 는 코드를 조정하지 않으면 절대로 도달하지 않습니다. 이것은 ipairs1..n-1 에서 반복하기 때문입니다. 여기서 n 은 발견 된 첫 번째 nil 의 위치입니다. 루아는 table[length-of-table + 1]nil 정의 table[length-of-table + 1] . 그래서 적절한 순서로, 루아가 말하자면, 세 항목으로 된 배열의 네 번째 항목을 얻으려고 할 때 반복이 멈 춥니 다.

언제?

스파 스 배열로 발생하는 문제의 가장 일반적인 두 가지 장소는 (i) 배열의 길이를 결정할 때와 (ii) 배열을 반복 할 때입니다. 특히:

  • 사용시 # 길이 연산자 길이 오퍼레이터는 먼저 카운팅 정지 이후 nil 알았다.
  • 위에서 언급 한 바와 같이 ipairs() 함수를 사용할 때 발견 된 첫번째 nil 에서 iterating을 멈 춥니 다.
  • 이 메소드는 발견 된 첫 번째 nil 에서 언 패킹을 중지하기 때문에 table.unpack() 함수를 사용할 때.
  • 위의 항목 중 하나에 (직접 또는 간접적으로) 액세스하는 다른 기능을 사용할 때.

이 문제를 피하려면 코드를 작성하여 테이블이 배열이 될 것으로 예상되는 경우 틈이 생기지 않도록해야합니다. 간격은 여러 가지 방법으로 도입 될 수 있습니다.

  • 배열에 잘못된 위치에 뭔가를 추가하는 경우.
  • 배열에 nil 값을 삽입 할 경우.
  • 배열에서 값을 제거하는 경우.

당신은 생각할지도 모르지만, "나는 그런 것들을 결코하지 않을 것이다." 음, 의도적으로는 아니지만, 여기에 상황이 잘못 될 수있는 구체적인 예가 있습니다. 루아의 select 와 Perl의 grep 과 같은 필터 메소드를 작성한다고 가정 grep . 메서드는 테스트 함수와 배열을 받아들입니다. 배열을 반복하면서 각 항목에 대한 테스트 메소드를 차례로 호출합니다. 항목이 통과되면 해당 항목이 메소드 끝에서 반환되는 결과 배열에 추가됩니다. 다음은 버그가있는 구현입니다.

local filter = function (fun, t)
  local res = {}
  for idx, item in ipairs(t) do
    if fun(item) then
      res[idx] = item
    end
  end

  return res
end

문제는 함수가 false 반환하면 시퀀스의 숫자를 건너 뜁니다. filter(isodd, {1,2,3,4,5,6,7,8,9,10}) 호출 : filter 전달 된 배열에 짝수가있을 때마다 리턴 된 테이블에 간격이 생길 것이다.

다음은 고정 된 구현입니다.

local filter = function (fun, t)
  local res = {}
  for _, item in ipairs(t) do
    if fun(item) then
      res[#res + 1] = item
    end
  end

  return res
end

  1. 표준 함수 사용 : table.insert(<table>, <value>) 항상 배열의 끝에 추가됩니다. table[#table + 1] = value 은이를위한 짧은 table[#table + 1] = value 입니다. table.remove(<table>, <index>) 는 다음의 모든 값을 다시 채워 간격을 채 웁니다 (속도를 느리게 할 수도 있음).
  2. 확인 nil 같은 것들을 피하고, 삽입하기 전에table.pack(function_call()) , 몰래 수있는 nil 우리 테이블에 값.
  3. 삽입 nil 확인하고 필요한 경우 모든 연속 값을 이동하여 간격을 채 웁니다.
  4. 가능한 경우 자리 표시 자 값을 사용하십시오. 예를 들어 0 또는 다른 자리 표시 자 값에 대해 nil 을 변경합니다.
  5. 격차를 피하는 것이 불가피한 경우,이를 상세히 문서화해야한다 (논평).
  6. __len() 메타 메소드를 작성하고 # 연산자를 사용하십시오.

6의 예.

tab = {"john", "sansa", "daenerys", [10] = "the imp"}
print(#tab) --> prints 3
setmetatable(tab, {__len = function() return 10 end})
-- __len needs to be a function, otherwise it could just be 10
print(#tab) --> prints 10
for i=1, #tab do print(i, tab[i]) end
--> prints:
-- 1 john
-- 2 sansa
-- 3 daenerys
-- 4 nil
-- ...
-- 10 the imp

for key, value in ipairs(tab) do print(key, value) end
--> this only prints '1 john \n 2 sansa \n 3 daenerys'

또 다른 방법은 pairs() 함수를 사용하고 비 정수 인덱스를 필터링하는 것입니다.

for key in pairs(tab) do
    if type(key) == "number" then
        print(key, tab[key]
    end
end
-- note: this does not remove float indices
-- does not iterate in order


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow