Таблицы. Что это такое и с чем его едят. Часть 1. — различия между версиями — S.T.A.L.K.E.R. Inside Wiki

Таблицы. Что это такое и с чем его едят. Часть 1. — различия между версиями

Материал из S.T.A.L.K.E.R. Inside Wiki

Перейти к: навигация, поиск
(оформил и сменил категорию..)
 
(не показаны 30 промежуточные версии 5 участников)
Строка 1: Строка 1:
'''Таблицы. Часть 1.'''
+
Итак. Большинство модеров которые начинают осваивать скриптинг сталкера сталкиваются с таким понятием как таблицы.
 +
==Таблицы==
 +
Таблица в '''Lua''' – это не только базовый тип данных. И даже не столько. Это фундаментальная основа языка, предопределяющая чуть ли не все возможности Lua.<br>
 +
Таблицы могут использоваться как обычные массивы, таблицы символов, множества, поля записей, деревья и так далее.<br>
 +
Причем, поскольку функции относятся к значениям первого класса, поля таблицы могут содержать и функции. Таким образом, таблицы могут хранить методы.
  
Итак. Большинство модеров которые начинают осваивать скриптинг сталкера сталкиваются с таким понятием как таблицы. Таблица - это локальная переменная в которой содержатся те или иные параметры/значения/наборы символов. К примеру:
+
Механизмы реализации типа «таблица» или ассоциативный массив в Lua очень эффективны.<br>
 +
Например, вычисление знаменитой рекурсивной функции Аккермана на Lua на порядок быстрее, чем на Perl и Ruby, и в пять раз – чем на Python.<br>
 +
Скорость вычисления хэш-функций (с использованием механизма хэширования и строятся Lua-таблицы) у Lua почти безупречна – интерпретируемые Lua-программы справляются с этой задачей менее чем в два раза медленнее написанных на языке C.
  
 +
==Определение==
 +
Таблица представляет собой ассоциативный массив (набор пар ключ-значение). Ключём (индексом) и значением может быть любой тип данных, используемых в Lua (за исключением '''nil''').<br>
  
local variables = {1, 2}
+
==Конструктор таблицы==
 +
Конструктор используется для создания таблиц и представляет собой список полей в фигурных скобках.<br>
 +
Поля отделяются друг от друга запятыми ("''','''") или точками с запятой ("''';'''"). При этом допускается наличие разделителя после последнего поля.
  
Примечание: таблицы можно использовать как за функцией, так и внутри неё.
+
Таблица может быть проинициализирована при создании (стандартный конструктор таблицы):
В нашем случае таблица содержит набор чисел 1 и 2. Что же с ними можно сделать?
+
<lua>t1 = {}                      -- пустая таблица
 +
t2 = { 1, 5, 7, 'abc' }      -- обычный массив
 +
t3 = { x = 7, y = "6" }      -- таблица с именованными полями '''x''' и '''y'''
 +
t4 = { 1, 'string', x = 77 } -- смешанная таблица
 +
t5 = { 1, xxx = 17, }        -- разделитель в конце допустим</lua>
  
function table()
+
Или заполнена позже, инициализируя каждую пару ключ-значение:
local variables= {1, 2} --наша таблица
+
<lua>t4      = {}
rezultat = variables[math.random(table.getn(variables))] --rezultat локальная переменная.
+
t4[1]   = 1        -- Определяет число 1 на место, индексируемое номером 1
if rezultat == 1 then
+
t4[2]   = 'string' -- Определяет строку 'string' на место, индексируемое номером 2
news_manager.send_tip(db.actor,lose_text, nil, nil, 10000) and db.actor:give_info_portion("lose") end
+
t4['x'] = 77      -- Определяет число 77 на место, индексируемое строкой 'x'</lua>
   if rezultat == 2 then
+
news_manager.send_tip(db.actor,win_text, nil, nil, 10000) and db.actor:give_info_portion("win") end end
+
  
Поясню: variables[math.random(table.getn(variables))] этот оператор позволяет взять рандомное значение из данной таблицы. Тобишь случайно взять либо число 1, либо число 2.
+
Для удобства работы можно вместо индексирования таблицы по имени (строке) использовать это имя как имя поля структуры:
 +
<lua>t4['x'] = 77
 +
t4.x    = 77</lua>
  
  if rezultat == 1 then
+
Эти две формы обращения полностью эквивалентны.
news_manager.send_tip(db.actor,lose_text, nil, nil, 10000) and db.actor:give_info_portion("lose") end
+
  if rezultat == 2 then
+
news_manager.send_tip(db.actor,win_text, nil, nil, 10000) and db.actor:give_info_portion("win") end
+
Определяет значение взятое из таблицы, и в зависимости от результата присылает нам то или иное сообщение (news_manager.send_tip(db.actor,***, nil, nil, 10000)) и даёт тот или иной инфопоршень (db.actor:give_info_portion("***") end).
+
  
 +
==Массив==
 +
Чтобы получить обычный массив (таблица t2) просто задаются значения элементов. Ключи будут установлены автоматически.<br>
 +
В Lua обычные массивы индексируются целыми, последовательно-нарастающими числами начиная с единицы.
 +
Примеры ниже эквивалентны.
 +
<lua>t2 = { 1, 5, 7, 'abc' }
  
'''=='''
+
t2 = { [1]=1, [2]=5, [3]=7, [4]='abc' }
  
Это оператор сравнения. В нашем случае это "равно". Так же есть операторы:
+
t2    = {}
 +
t2[1] = 1
 +
t2[2] = 5
 +
t2[3] = 7
 +
t2[4] = 'abc'</lua>
 +
 
 +
Пока возможно, Lua внутри себя таблицу хранит как массив, а не как хэш - таблицу.<br>
 +
В этом случае доступ к элементам таблицы происходит почти так же быстро, как в массивах Си. Поэтому без особой нужды не нужно превращать массив в хэш.<br>
 +
Для того, чтобы не нарушать структуру при добавлении и удалении элементов массива стоит пользоваться  библиотекой Lua '''table'''.
 +
<lua>local t = {1, 2, 3, 4, 5}
 +
table.insert(t, 6)    -- добавляет элемент в конец массива.                                      t = {1, 2, 3, 4, 5, 6}
 +
table.insert(t, 0, 1) -- вставляет элемент по индексу, сдвигая оставшиеся элементы массива.      t = {0, 1, 2, 3, 4, 5, 6}
 +
table.remove(t, 3)    -- удаляет из таблицы элемент по индексу 3 и сдвигает оставшиеся элементы. t = {0, 1, 3, 4, 5, 6}</lua>
 +
 
 +
===Размер массива===
 +
Получение размера массива выполняется оператором '''#''':
 +
<lua>local count = #t</lua>
 +
Оператор '''#''' возвращает целое число n, такое, что '''t[n]''' не '''nil''', и '''t[n + 1]''' равно '''nil'''. Другими словами оператор '''#''', возвращает  максимальный индекс непрерывной последовательности ключей от начала массива.<br>Соответственно, для таблицы:
 +
<lua>t = {1, [100] = 2}</lua>
 +
длина будет равна 1, поскольку '''t[1]''' не '''nil''', а '''t[1 + 1]''' равно '''nil'''.
 +
Для обычного массива, оператор '''#''' вернет количество элементов в массиве.
 +
 
 +
===Обход элементов массива===
 +
Для обхода элементов массива используется как простая, так и расширенная форма записи оператора '''for''' (см. http://www.lua.ru/doc/2.4.5.html )
 +
Обычно применяется '''простая форма''':
 +
<lua>for i=1,#t do
 +
...
 +
end</lua>
 +
 
 +
Цикл обойдет все поля массива от поля с индексом 1 (i=1) до последнего индекса поля (#t), определяемого оператором '''#''' (см. "Размер массива")<br>
 +
Или же '''расширенная форма''' с использованием функции-итератора '''ipairs''':
 +
<lua>for key,value in ipairs(t) do
 +
...
 +
end</lua>
 +
 
 +
Функция-итератор '''ipairs''' возвращает два значения. Первое - ключ этого поля, второе - значение.<br>
 +
При нахождении каждого последующего поля, эти значения присваиваются переменным '''key''' и '''value'''.
 +
 
 +
==Хэш-таблицы==
 +
Талицы, не попадающие под определение массива, являются хэш-таблицами. Индексами таких таблиц являются объекты различных типов (кроме '''nil''').<br>
 +
Узнать количество элементов такой таблицы, кроме как обойдя их все, нельзя.
 +
 
 +
===Строение хэш-таблицы===
 +
Любая хэш-таблица состоит из двух частей - индексированной и именованной.
 +
Индексированная часть подчиняется законам массива (см. выше), именованная - включает индексы(ключи), которые невозможно включить в индексированную часть, не нарушая её строения.
 +
Например для таблицы:
 +
<lua>t = {a=1,b=2}</lua>
 +
существует индексированная часть. Подтвердить это можно вызвав оператор '#', предназначенный для определения длины:
 +
<lua>print(#t) --> 0</lua>
 +
Т.е. оператор '#' определил (хоть и нулевую, но) длину. Проверив таблицу:
 +
<lua>t = {a=1,b=2, 'one'}</lua>
 +
Длина будет равна 1, т.к. появилось поле с индексом - [1], и значением - 'one' (автоматическое назначение индекса. см. выше)
 +
Ни одно из двух других полей этой таблицы не может быть частью массива, вместе с полем [1]='one', т.к. их индексы не соответствуют индексу с номером [2].
 +
Поэтому :
 +
* часть 'one' будет индексированной частью.
 +
* часть a=1 и b=2 именованной
 +
 
 +
===Обход элементов хэш-таблицы===
 +
Обход осуществляется только '''расширенной формой''' оператора '''for''' с использование функции-итератора '''pairs'''.<br>
 +
Эта функция позволяет обойти элементы любых таблиц (включая массивы). Возвращаемые значение такие-же, как и для функции ''ipairs'':
 +
<lua>for k,v in pairs(t) do
 +
...
 +
end</lua>
 +
 
 +
При этом '''всегда''' сначала определяются поля индексированной части как массива, а затем именованной.
 +
 
 +
Об ошибках, связанных с циклами по таблицам см. в статье '''KamikaZze''' [[ Как писать скрипты, не приводящие к вылетам и бою сейвов#Смертельные циклы по таблицам|здесь]]
 +
 
 +
Хотя лично я не согласен с утверждением, что "ни при каких обстоятельствах не используйте внутри этого цикла удаление/добавление строк" и "следует использовать только для операций, не изменяющих структуру изменяемой таблицы!".
 +
Правильнее было бы сказать, что "ни при каких обстоятельствах не изменяйте длину массива внутри этого цикла".
 +
Т.е. "смертельный" вариант:
 +
<lua>for i = i, #t do
 +
table.remove(t,i)
 +
end</lua>
 +
 
 +
в, котором с каждым "оборотом" уменьшается длина массива, становится самым быстрым и оптимальным в случае...ну, например, реверсирования массива:
 +
<lua>local len=#t
 +
for i = len-1, 1, -1 do
 +
t[len] = table.remove(t,i)
 +
end</lua>
 +
в котором длина массива не изменяется.
 +
 
 +
''to be continue ...''
 +
 
 +
'''Nazgool'''
 +
 
 +
P.S. То, что ниже, это остатки первоначальной статьи, верхнюю часть которой я безжалостно вырезал за несоответствие названию статьи.
 +
 
 +
---------------------------------------------------------------------------------------------------------------------------------
 +
 
 +
К примеру:
 +
<lua>local variables = {1, 2}</lua>
 +
 
 +
''Примечание'': таблицы можно использовать как за функцией, так и внутри неё.
 +
В нашем случае таблица содержит набор чисел 1 и 2. Что же с ними можно сделать?
 +
<lua>function table()
 +
local variables= {1, 2} --наша таблица
 +
rezultat = variables[math.random(table.getn(variables))] --rezultat локальная переменная.
 +
if rezultat == 1 then
 +
news_manager.send_tip(db.actor,lose_text, nil, nil, 10000) and db.actor:give_info_portion("lose")
 +
end
 +
if rezultat == 2 then
 +
news_manager.send_tip(db.actor,win_text, nil, nil, 10000) and db.actor:give_info_portion("win")
 +
end
 +
end</lua>
 +
 
 +
Поясню: '''variables[math.random(table.getn(variables))]''' этот оператор позволяет взять рандомное значение из данной таблицы. Тобишь случайно взять либо число 1, либо число 2.
 +
<lua>if rezultat == 1 then
 +
news_manager.send_tip(db.actor,lose_text, nil, nil, 10000) and db.actor:give_info_portion("lose")
 +
end
 +
if rezultat == 2 then
 +
news_manager.send_tip(db.actor,win_text, nil, nil, 10000) and db.actor:give_info_portion("win")
 +
end</lua>
 +
Определяет значение взятое из таблицы, и в зависимости от результата присылает нам то или иное сообщение (news_manager.send_tip(db.actor,***, nil, nil, 10000)) и даёт тот или иной инфопоршень (db.actor:give_info_portion("***") end).
 +
 
 +
 
 +
'''==''' - это оператор сравнения. В нашем случае это "равно". Так же есть операторы:
  
 
'''>''' - больше.
 
'''>''' - больше.
Строка 44: Строка 175:
  
 
На этом примере вы можете создать простейшую функцию спауна:
 
На этом примере вы можете создать простейшую функцию спауна:
 +
<lua>local stalker_types  = {"bread", "kolbasa", "conserva", "vodka"}
  
local stalker_types  = {"bread", "kolbasa", "conserva", "vodka"}
+
function spawn_item()  
 
+
alife():create(stalker_types[math.random(4)],vector():set(-0.112,0.477,-215.563),174943,265)
function spawn_item()  
+
end</lua>
alife():create(stalker_types[math.random(4)],vector():set(-0.112,0.477,-215.563),174943,265)
+
end
+
  
 
Как видите таблица используется вне функции, но можно и в самой функции. В данном примере в определённой точке с координатами (-0.112,0.477,-215.563),174943,265) заспаунится определённый предмет из списка. В эту таблицу можно внести как сталкеров, так и мутантов.
 
Как видите таблица используется вне функции, но можно и в самой функции. В данном примере в определённой точке с координатами (-0.112,0.477,-215.563),174943,265) заспаунится определённый предмет из списка. В эту таблицу можно внести как сталкеров, так и мутантов.
  
'''Позже продолжу. iDreD aka кровоSTALKER.'''
+
Автор: '''iDreD aka кровоSTALKER.'''
  
[[Категория:Скрипты]]
+
[[Категория: Скрипты]]

Текущая версия на 12:34, 23 сентября 2014

Итак. Большинство модеров которые начинают осваивать скриптинг сталкера сталкиваются с таким понятием как таблицы.

Таблицы

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

Механизмы реализации типа «таблица» или ассоциативный массив в Lua очень эффективны.
Например, вычисление знаменитой рекурсивной функции Аккермана на Lua на порядок быстрее, чем на Perl и Ruby, и в пять раз – чем на Python.
Скорость вычисления хэш-функций (с использованием механизма хэширования и строятся Lua-таблицы) у Lua почти безупречна – интерпретируемые Lua-программы справляются с этой задачей менее чем в два раза медленнее написанных на языке C.

Определение

Таблица представляет собой ассоциативный массив (набор пар ключ-значение). Ключём (индексом) и значением может быть любой тип данных, используемых в Lua (за исключением nil).

Конструктор таблицы

Конструктор используется для создания таблиц и представляет собой список полей в фигурных скобках.
Поля отделяются друг от друга запятыми (",") или точками с запятой (";"). При этом допускается наличие разделителя после последнего поля.

Таблица может быть проинициализирована при создании (стандартный конструктор таблицы):

t1 = {}                      -- пустая таблица
t2 = { 1, 5, 7, 'abc' }      -- обычный массив
t3 = { x = 7, y = "6" }      -- таблица с именованными полями '''x''' и '''y'''
t4 = { 1, 'string', x = 77 } -- смешанная таблица
t5 = { 1, xxx = 17, }        -- разделитель в конце допустим

Или заполнена позже, инициализируя каждую пару ключ-значение:

t4      = {}
t4[1]   = 1        -- Определяет число 1 на место, индексируемое номером 1
t4[2]   = 'string' -- Определяет строку 'string' на место, индексируемое номером 2
t4['x'] = 77       -- Определяет число 77 на место, индексируемое строкой 'x'

Для удобства работы можно вместо индексирования таблицы по имени (строке) использовать это имя как имя поля структуры:

t4['x'] = 77
t4.x    = 77

Эти две формы обращения полностью эквивалентны.

Массив

Чтобы получить обычный массив (таблица t2) просто задаются значения элементов. Ключи будут установлены автоматически.
В Lua обычные массивы индексируются целыми, последовательно-нарастающими числами начиная с единицы. Примеры ниже эквивалентны.

t2 = { 1, 5, 7, 'abc' }
 
t2 = { [1]=1, [2]=5, [3]=7, [4]='abc' }
 
t2    = {}
t2[1] = 1
t2[2] = 5
t2[3] = 7
t2[4] = 'abc'

Пока возможно, Lua внутри себя таблицу хранит как массив, а не как хэш - таблицу.
В этом случае доступ к элементам таблицы происходит почти так же быстро, как в массивах Си. Поэтому без особой нужды не нужно превращать массив в хэш.
Для того, чтобы не нарушать структуру при добавлении и удалении элементов массива стоит пользоваться библиотекой Lua table.

local t = {1, 2, 3, 4, 5}
table.insert(t, 6)    -- добавляет элемент в конец массива.                                      t = {1, 2, 3, 4, 5, 6}
table.insert(t, 0, 1) -- вставляет элемент по индексу, сдвигая оставшиеся элементы массива.      t = {0, 1, 2, 3, 4, 5, 6}
table.remove(t, 3)    -- удаляет из таблицы элемент по индексу 3 и сдвигает оставшиеся элементы. t = {0, 1, 3, 4, 5, 6}

Размер массива

Получение размера массива выполняется оператором #:

local count = #t

Оператор # возвращает целое число n, такое, что t[n] не nil, и t[n + 1] равно nil. Другими словами оператор #, возвращает максимальный индекс непрерывной последовательности ключей от начала массива.
Соответственно, для таблицы:

t = {1, [100] = 2}

длина будет равна 1, поскольку t[1] не nil, а t[1 + 1] равно nil. Для обычного массива, оператор # вернет количество элементов в массиве.

Обход элементов массива

Для обхода элементов массива используется как простая, так и расширенная форма записи оператора for (см. http://www.lua.ru/doc/2.4.5.html ) Обычно применяется простая форма:

for i=1,#t do
...
end

Цикл обойдет все поля массива от поля с индексом 1 (i=1) до последнего индекса поля (#t), определяемого оператором # (см. "Размер массива")
Или же расширенная форма с использованием функции-итератора ipairs:

for key,value in ipairs(t) do
...
end

Функция-итератор ipairs возвращает два значения. Первое - ключ этого поля, второе - значение.
При нахождении каждого последующего поля, эти значения присваиваются переменным key и value.

Хэш-таблицы

Талицы, не попадающие под определение массива, являются хэш-таблицами. Индексами таких таблиц являются объекты различных типов (кроме nil).
Узнать количество элементов такой таблицы, кроме как обойдя их все, нельзя.

Строение хэш-таблицы

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

t = {a=1,b=2}

существует индексированная часть. Подтвердить это можно вызвав оператор '#', предназначенный для определения длины:

print(#t) --> 0

Т.е. оператор '#' определил (хоть и нулевую, но) длину. Проверив таблицу:

t = {a=1,b=2, 'one'}

Длина будет равна 1, т.к. появилось поле с индексом - [1], и значением - 'one' (автоматическое назначение индекса. см. выше) Ни одно из двух других полей этой таблицы не может быть частью массива, вместе с полем [1]='one', т.к. их индексы не соответствуют индексу с номером [2]. Поэтому :

  • часть 'one' будет индексированной частью.
  • часть a=1 и b=2 именованной

Обход элементов хэш-таблицы

Обход осуществляется только расширенной формой оператора for с использование функции-итератора pairs.
Эта функция позволяет обойти элементы любых таблиц (включая массивы). Возвращаемые значение такие-же, как и для функции ipairs:

for k,v in pairs(t) do
...
end

При этом всегда сначала определяются поля индексированной части как массива, а затем именованной.

Об ошибках, связанных с циклами по таблицам см. в статье KamikaZze здесь

Хотя лично я не согласен с утверждением, что "ни при каких обстоятельствах не используйте внутри этого цикла удаление/добавление строк" и "следует использовать только для операций, не изменяющих структуру изменяемой таблицы!". Правильнее было бы сказать, что "ни при каких обстоятельствах не изменяйте длину массива внутри этого цикла". Т.е. "смертельный" вариант:

for i = i, #t do
table.remove(t,i)
end

в, котором с каждым "оборотом" уменьшается длина массива, становится самым быстрым и оптимальным в случае...ну, например, реверсирования массива:

local len=#t
for i = len-1, 1, -1 do
t[len] = table.remove(t,i)
end

в котором длина массива не изменяется.

to be continue ...

Nazgool

P.S. То, что ниже, это остатки первоначальной статьи, верхнюю часть которой я безжалостно вырезал за несоответствие названию статьи.


К примеру:

local variables = {1, 2}

Примечание: таблицы можно использовать как за функцией, так и внутри неё. В нашем случае таблица содержит набор чисел 1 и 2. Что же с ними можно сделать?

function table()
local variables= {1, 2} --наша таблица
rezultat = variables[math.random(table.getn(variables))] --rezultat локальная переменная.
if rezultat == 1 then
news_manager.send_tip(db.actor,lose_text, nil, nil, 10000) and db.actor:give_info_portion("lose")
end
if rezultat == 2 then
news_manager.send_tip(db.actor,win_text, nil, nil, 10000) and db.actor:give_info_portion("win") 
end
end

Поясню: variables[math.random(table.getn(variables))] этот оператор позволяет взять рандомное значение из данной таблицы. Тобишь случайно взять либо число 1, либо число 2.

if rezultat == 1 then
news_manager.send_tip(db.actor,lose_text, nil, nil, 10000) and db.actor:give_info_portion("lose")
end
if rezultat == 2 then
news_manager.send_tip(db.actor,win_text, nil, nil, 10000) and db.actor:give_info_portion("win")
end

Определяет значение взятое из таблицы, и в зависимости от результата присылает нам то или иное сообщение (news_manager.send_tip(db.actor,***, nil, nil, 10000)) и даёт тот или иной инфопоршень (db.actor:give_info_portion("***") end).


== - это оператор сравнения. В нашем случае это "равно". Так же есть операторы:

> - больше.

< - меньше.

>= - больше или равно.

<= - меньше или равно.

~= - не равно.


Сравнивать можно только числа или локальные переменные с присвоенными к ним числами.

На этом примере вы можете создать простейшую функцию спауна:

local stalker_types  = {"bread", "kolbasa", "conserva", "vodka"}
 
function spawn_item() 
alife():create(stalker_types[math.random(4)],vector():set(-0.112,0.477,-215.563),174943,265)
end

Как видите таблица используется вне функции, но можно и в самой функции. В данном примере в определённой точке с координатами (-0.112,0.477,-215.563),174943,265) заспаунится определённый предмет из списка. В эту таблицу можно внести как сталкеров, так и мутантов.

Автор: iDreD aka кровоSTALKER.

Другие места
LANGUAGE