수색…
통사론
- 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"
즉각적인 질문에 답하기 위해, ipairs
이 ipairs
후 멈출 것입니다. pets[12]
"금붕어" pets[12]
는 코드를 조정하지 않으면 절대로 도달하지 않습니다. 이것은 ipairs
가 1..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
팁
- 표준 함수 사용 :
table.insert(<table>, <value>)
항상 배열의 끝에 추가됩니다.table[#table + 1] = value
은이를위한 짧은table[#table + 1] = value
입니다.table.remove(<table>, <index>)
는 다음의 모든 값을 다시 채워 간격을 채 웁니다 (속도를 느리게 할 수도 있음). - 확인
nil
같은 것들을 피하고, 삽입하기 전에 값table.pack(function_call())
, 몰래 수있는nil
우리 테이블에 값. - 삽입 후
nil
값 을 확인하고 필요한 경우 모든 연속 값을 이동하여 간격을 채 웁니다. - 가능한 경우 자리 표시 자 값을 사용하십시오. 예를 들어
0
또는 다른 자리 표시 자 값에 대해nil
을 변경합니다. - 격차를 피하는 것이 불가피한 경우,이를 상세히 문서화해야한다 (논평).
-
__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