Поиск…


Синтаксис

  • ipairs (numeric_table) - таблица Lua с числовыми индексами итератор
  • пары (input_table) - общий итератор таблицы Lua
  • key, value = next (input_table, input_key) - Селектор значений таблицы Lua
  • table.insert (input_table, [position], value) - вставить указанное значение во входную таблицу
  • removed_value = table.remove (input_table, [position]) - pop last или удалить значение, заданное положением

замечания

Таблицы - единственная встроенная структура данных, доступная в Lua. Это либо элегантная простота, либо запутанность, в зависимости от того, как вы на это смотрите.

Таблица Lua представляет собой набор пар ключ-значение, где ключи уникальны, и ни ключ, ни значение не равны nil . Таким образом, таблица Lua может напоминать словарь, hashmap или ассоциативный массив с других языков. Многие структурные шаблоны могут быть построены с использованием таблиц: стеков, очередей, наборов, списков, графиков и т. Д. Наконец, таблицы могут использоваться для создания классов в Lua и для создания модульной системы.

Lua не применяет никаких конкретных правил использования таблиц. Элементы, содержащиеся в таблице, могут быть смесью типов Lua. Так, например, одна таблица может содержать строки, функции, логические значения, числа и даже другие таблицы как значения или ключи.

Говорят, что таблица Lua с последовательными положительными целыми ключами, начинающимися с 1, имеет последовательность. Ключи-значения с положительными целыми ключами являются элементами последовательности. Другие языки называют это массивом на основе 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
}

Итерирующие столы

Стандартная библиотека Lua предоставляет функцию pairs которая выполняет итерации по ключам и значениям таблицы. При повторении с pairs нет определенного порядка для обхода, даже если ключи таблицы являются числовыми .

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

Для таблиц с использованием цифровых клавиш Lua предоставляет функцию ipairs . Функция ipairs всегда будет перебираться из table[1] , table[2] и т. Д. До тех пор, пока не будет найдено первое значение nil .

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

Последний способ перебора таблиц - использовать next селектор в родовом for цикла . Подобно pairs нет определенного порядка для обхода. (The pairs метод использует next внутри. Таким образом , используя next по существу более ручной вариант pairs . См pairs в справочном руководстве по next Lua в и next в справочном руководстве по Lua в для более подробной информации.)

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 , используют в качестве ключей увеличение целых значений; синтаксис записи использует имя поля в виде строки.

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.insert() , где индекс не указан, и данное значение добавляется в конец таблицы (индекс # #table + 1 ).

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() предоставляет семантику стека таблицам. Используя это, вы можете написать код, как показано ниже.

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

Он реализует «Фишер-Йейтс», возможно, неэффективно. Он использует table.insert() чтобы добавить случайно извлеченный элемент в конец той же таблицы, а table.remove() для случайного извлечения элемента из оставшейся неповрежденной части таблицы.

Избегать пробелов в таблицах, используемых в качестве массивов

Определение наших условий

Под массивом здесь мы понимаем таблицу Lua, используемую в качестве последовательности. Например:

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

Мы используем эту таблицу как последовательность: группу элементов, на которые ссылаются целые числа. Многие языки называют этот массив, и мы тоже. Но, строго говоря, в Lua нет такой вещи, как массив. Существуют только таблицы, некоторые из которых являются подобными массиву, некоторые из которых являются хэш-подобными (или похожими на словари, если вы предпочитаете), а некоторые из них смешаны.

Важным моментом в нашем массиве pets является отсутствие пробелов. Первым пунктом, pets[1] , является строка «собаки», второй пункт, pets[2] , это строка «кошки», а последний предмет, pets[3] , - «птицы». Стандартная библиотека Lua и большинство модулей, написанных для Lua, принимают 1 как первый индекс для последовательностей. Таким образом, безщеточный массив имеет элементы из 1..n не пропуская никаких чисел в последовательности. (В предельном случае n = 1 , и массив имеет только один элемент в нем).

Lua предоставляет встроенные функции 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 остановятся после птиц; «Золотая рыбка» у pets[12] никогда не будет достигнута, если мы не скорректируем наш код. Это связано с тем, что ipairs выполняет ipairs с 1..n-1 где n - позиция первого найденного nil . Lua определяет table[length-of-table + 1] равной nil . Поэтому в правильной последовательности итерация останавливается, когда Lua пытается получить, скажем, четвертый элемент в массиве из трех элементов.

Когда?

Два наиболее распространенных места для возникновения проблем с разреженными массивами: (i) при попытке определить длину массива и (ii) при попытке выполнить итерацию по массиву. Особенно:

  • При использовании оператора длины # так как оператор длины останавливается при первом найденном nil .
  • При использовании функции ipairs() поскольку, как упоминалось выше, она прекращает итерацию при обнаружении первого nil .
  • При использовании функции table.unpack() поскольку этот метод останавливает распаковку при обнаружении первого nil .
  • При использовании других функций, которые (прямо или косвенно) доступны для любого из вышеперечисленных.

Чтобы избежать этой проблемы, важно написать свой код, чтобы, если вы ожидаете, что таблица будет массивом, вы не вводите пробелы. Пробелы можно вводить несколькими способами:

  • Если вы добавите что-то в массив в неправильном положении.
  • Если вы вставляете значение nil в массив.
  • Если вы удалите значения из массива.

Вы можете подумать: «Но я никогда не сделаю ничего из этого». Ну, не намеренно, но вот конкретный пример того, как все может пойти не так. Представьте, что вы хотите написать метод фильтрации для Lua, например, Ruby's select и Perl's 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.remove(<table>, <index>) вернет все следующие значения, чтобы заполнить пробел (что также может замедлить работу).
  2. Проверяйте значения nil перед вставкой, избегая таких вещей, как table.pack(function_call()) , которые могут подкрасить значения nil в нашу таблицу.
  3. Проверяйте значения nil после вставки и, при необходимости, заполняя зазор, сдвигая все последовательные значения.
  4. Если возможно, используйте значения заполнитель. Например, измените значение nil на 0 или какое-либо другое значение-заполнитель.
  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