Добавлен , опубликован

Lua-программирование для чайников

Содержание:
Таблицы в целом — самый важный тип данных в Lua.

14.1. Массивы.

Первое, что рассмотрим, — массивы. Массивы — это таблицы с числовой индексацией. Массив начинается с его названия, потом ставится равно и в фигурных скобках значения.
Каждый элемент массива пишется в кавычках и разделяется запятой.
Синтаксис:
... = {...};
Самый простой массив выглядит так:
m = {};
«Заполнять» массивы можно так:
m = {"one","two","three",}; 
--обратиться можно как m[1], m[2], m[3] индексы данных массивов нумеруются с единицы
После последнего элемента запятая не обязательна.
Если это был бы варкрафт, пример заполнение поля абилок:
list_ability = {'A000','A001','A002',}; --забиваем строчные равкоды
--обратиться можно как list_ability[1], list_ability[2], list_ability[3] индексы данных массивов нумеруются с единицы
Внимание: Все строки записываются внутри двойных кавычек "строка 1" или ординарных кавычек 'строка 2'. пример:
m = {'Привет',"Как дела?"}
print(m[1]) --печатает как "Привет"
print(m[2]) --печатает как "Как дела?"
Внутри таких скобок можно забивать через запятую любые данные: строки, числа, таблицы, boolean и пр.
m = {2022,'xgm',"Скорпи за вам наблюдает",.5,{},false,hh,22}
print(m[1]) --печатает число "2020"
print(m[2]) --печатает строку "xgm"
print(m[3]) --печатает строку "Скорпи за вам наблюдает"
print(m[4]) --печатает число "0.5"
print(m[5]) --печатает таблицу "table: 0x561981ffb2a0"
print(m[6]) --печатает boolean "false"
print(m[7]) --печатает "nil"
print(m[8]) --печатает число "22"
print(m[9]) --печатает "nil", тк дальше ничего не указано
Обратите внимание выше на код. Строка "hh" программа засчитала не как строчку, а как переменную hh. И в ней ничего не указано, поэтому m[7] вернуло nil. Помните, что если строка не записана внутри кавычек, то превращается в переменную.
Можно внутри указать значение через оператор присваивания "=" через запятую. Пример:
m = {a=9,b='Утро',c={}}
--однако, индексация не работает. вы не сможете обратиться как m[1], m[2], m[3] возвращает всем трем результат nil.

--обратиться можно по-другому:
print(m.a) --печатает число "9"
print(m.b) --печатает строку "Утро"
print(m.c) --печатает таблицу "table: 0x561981ffb2a0"
Я заметил, что если в ряд указать данные. И где-то в этом списке указать переменную со значением, то эта переменная опускается. Пример:
m = {1000,b='Тыща босс',{}}
print(m[1]) --печатает число "1000"
print(m[2]) --печатает таблица table: 0x55bb7fc38dc0
print(m[3]) --печатает "nil"

print(m.b) --печатает "Тыща босс"
как видно переменная b уже не индексируется, и опускается.

Вариантивность записи таблиц с внутри табличными переменными

Аналогично, можно также создать таблицу, и прописать координаты:
unit = {}; --создаем таблицу
--x,y как отдельные именные переменные
unit.x = GetUnitX(u)
unit.y = GetUnitY(u)
А можно итак записать (аналогия выше):
unit = {x= GetUnitX(u), y = GetUnitY(u)}; --сразу внутри скобок прописываем
Кому не удобно, можно даже в ряд прописывать данные, типа так:
unit = {
 --сразу внутри скобок прописываем
x= GetUnitX(u), 
y = GetUnitY(u),
z = GetUnitZ(u),
};
Если можно, то можно на тип указать разные данные:
пример нужны для кнопки требования: золото, древесина, мана, пища, список здании
local requirements = {}
requirements['Hate']={
    gold_cost = 11,
    lumber_cost = 22,
    mana_cost = 200,
    food_cost = 70,
    buildings_required = {'hhou'}
}

print(requirements['Hate'].gold_cost) --печатает 11
или пример записи координат:
	записывать координаты можно таким образом:
	--база данных координат иконок зданий
	ButtonList = {
            ["default"] = { pos_x = 0, pos_y = 0 },
            --peasant (human)
            ['htow'] = { pos_x = 0, pos_y = 0 },
            ['hbar'] = { pos_x = 1, pos_y = 0 },
            ['hlum'] = { pos_x = 2, pos_y = 0 },
            ['hbla'] = { pos_x = 3, pos_y = 0 },
            ['hhou'] = { pos_x = 0, pos_y = 1 },
            ['halt'] = { pos_x = 1, pos_y = 1 },
            ['hars'] = { pos_x = 2, pos_y = 1 },
            ['harm'] = { pos_x = 3, pos_y = 1 },    
            ['hwtw'] = { pos_x = 0, pos_y = 2 },
            ['hgra'] = { pos_x = 1, pos_y = 2 },
            ['hvlt'] = { pos_x = 2, pos_y = 2 },
            --peon (orc)
            ['ogre'] = { pos_x = 0, pos_y = 0 },
            ['obar'] = { pos_x = 1, pos_y = 0 },
            ['ofor'] = { pos_x = 2, pos_y = 0 },
            ['owtw'] = { pos_x = 3, pos_y = 0 },
            ['otrb'] = { pos_x = 0, pos_y = 1 },
            ['oalt'] = { pos_x = 1, pos_y = 1 },
            ['osld'] = { pos_x = 2, pos_y = 1 },
            ['obea'] = { pos_x = 3, pos_y = 1 },    
            ['otto'] = { pos_x = 0, pos_y = 2 },
            ['ovln'] = { pos_x = 1, pos_y = 2 }
        }
	--обратиться можно так:
    local button_data = ButtonList[type_building] or ButtonList['default']
    local x,y = button_data.pos_x or 0, button_data.pos_y or 0
Индексация массивов — это следующий параграф.

14.1.1. Индексация массивов.

Индексация массивов в Lua начинается не с нуля, как в некоторых других языках (в том же C), а с единицы.
Это не значит, что вы не сможете нумеровать с нуля. Можно. Просто если обрабатывать все таблицу может выйти боком, как было у меня. Если у вас задаются данные построчно, а не нумеруются, то тут нумерация будет с 1. И поможет здесь не арифметический цикл for .. do, а оператор pairs() (см. 14.1.4)
Вот эта запись:
m = {"one","two","three",};
Полностью эквивалентна этой:
m = {[1] = "one",[2] = "two",[3] = "three",};
Подобная индексация нежелательна:
m = {[1] = "one",[3] = "three",};
Почему? Это следующий параграф.
Но если надо, можно так:
m = {[1] = "one",[2] = nil,[3] = "three",};
У каждого поля таблицы может иметь так и название, так и номер. Если вы дадите полю/ячейке имя, то он автоматически нумеруется. А нумерация идет не с нуля, а с единицы.

14.1.2. Определение длины массива.

Оператор длины массива возвращает наибольший индекс элементов массива. В более старых версиях Lua этим оператором служит функция table.getn(), в более новых же можно использовать оператор «#»,
функцию же можно по-прежнему использовать.
Вот простой пример:
m = {[1] = "one",[2] = "two",[3] = "three",};  
print(#m);  
print(table.getn(m));
Здесь напечатается 3, поскольку в массиве три элемента.
Чтобы не было проблем с версиями Lua, дальше уже будет писаться только table.getn().
А что напечатается здесь?:
m = {[1] = "one",[3] = "three",};  
print(table.getn(m));
А напечатается здесь 1, поскольку индекс два равен nil.
А тут вовсе напечатается 0:
m = {[2] = "two",[3] = "three",};  
print(table.getn(m));
Поскольку индекс один равен nil.
Тут тоже 0:
m = {1 = "one",2 = "two", 3 = "three",};  
print(table.getn(m));
Потому что тут вообще ошибка вылезет.
В общем, чтобы оператор длины работал корректно, нужно указывать все индексы правильно, или не указывать вообще, — Lua это сам сделает.
Правильный код:
m = {[1] = "one",[2] = "two", [3] = "three",};  
print(table.getn(m));

14.1.3. Обращение к элементам массива.

Обращение к элементам массива происходит так:
m = {"one","two","three",};  

if m[1] then  
  print(m[1]); -- "one"  
end  
if m[2] then  
  print(m[2]); -- "two"  
end  
if m[3] then  
  print(m[3]); -- "three"  
end
Пишется название массива, затем в квадратных скобках указывается его индекс.

14.1.4. Цикл обхода всех элементов массива.

Вот, собственно, один из двух циклов, упомянутый в §10.5.
Для обхода всех элементов массива используется расширенный арифметический цикл for вместе с переменной ipairs.
Синтаксис:
for ...,... in ipairs(...) do  
  ...  
end
То, что перед запятой, — индекс массива, а после — его значение (для примера ниже значение первого индекса — "one").
Пример:
m = {"one","two","three",};  
for i,v in ipairs(m) do  
  print(i.." is "..v);  
end
Как и в случае с арифметическим циклом, две эти переменные являются локальными и могут принимать любые другие значения:
for h,z in ipairs(m) do  
  ...  
end
Иногда в получении индекса либо значения нет необходимости, в таком случае, переменная заменяется на подчёркивание:
for _,v in ipairs(m) do  
  ...  
end

14.1.4.1. Обход элементов массива иными способами.

Для обхода все элементов можно и не пользоваться ipairs и for .. in, а просто воспользоваться арифметическим for и оператором длины:
for i=1,table.getn(m) do  
  ...  
end
В качестве индекса используется, как понятно, переменная i.
Только стоит учесть, что при использовании этого варианта получение значения массива будет недоступным.
А вот может случиться такая ситуация, что нескольким (но не всем) элементам массива необходимо будет присвоить одно и то же значение. Можно, конечно, воспользоваться примитивными методами, типа:
m[5] = nil;  
m[6] = nil;
И так далее. Но намного быстрее воспользоваться арифметическим for:
for i=5,10 do  
  m[i] = nil;  
end
Для всех элементов можно по стандарту:
for i,_ in ipairs(m) do
m[i] = nil;
end
Тут уже кому как.

14.1.5. Массивы внутри массивов.

Если есть необходимость в этом, внутри пары фигурных скобок создаётся ещё одна пара (просто фигурные скобки, без названия), и туда как обычно записываются элементы нового массива.
Пример:
m = {{"one","half",},"two","three",};
Обращение к элементам массив Color = { {},{},{} } -- > равнозначно Color = { {}=1,{}=2,{}=3 }
а, который внутри другого массива, происходит так: сначала в квадратных скобках пишется индекс внешнего массива, в котором расположен внутренний, затем уже так же индекс элемента внутреннего массива:
print(m[1][1]); -- "one"  
print(m[1][2]); -- "half"
print(m[2]); -- "two"
print(m[3]); -- "three"
или пример:
Сolor = { } --одномерный массив, создаем таблицу
Color = { {},{},{} }

--равнозначный
Color = { [1]={},[2]={},[3]={} }

--или по простому, можно разложить так:
Сolor = {} --одномерный массив, создаем таблицу
Color[1]= ...
Color[2]= ...
Color[3]= ...

--для создания многомерного массива
Color[1] = {} --двумерный массив, создаем таблицу
Color[1][1]= 1
Color[1][2]= 2
Color[1][3]= 3
Color[2] = {} --двумерный массив, создаем таблицу
Color[2][1]= “привет”
Color[2][2]= “как дела?”
Color[2][3]= “что делаете?”
Color[3] = {} --двумерный массив, создаем таблицу
Color[3][1]= x
Color[3][2]= y
Color[3][3]= z

--точно также можно не использовать скобки квадратные с цифрами
a = {}
a.b = {} --двухмерный массив, тот же самое что и a[“b”]={}
a.b.c = {} --трехмерный массив, то же самое что и a[“b”][“с”]={}
--запомните, что названные ячейки тоже номеруются. 
Обход такого массива выглядит так:
for i,_ in ipairs(m) do  
  m[1][i] = nil  
end
print(m[1][1]); -- nil  
print(m[1][2]); -- nil
Так можно создавать огромное количество массивов, каждый из которых будет внутри предыдущего:
m = {{{{{nil}}}}};  

print(m[1][1][1][1][1]); -- nil

14.2. Таблицы.

Таблицы — это набор пар «ключ — значение». Конструкция такая же, как и у массивов.
Вот пример:
t = {  
  one = 1,  
  two = 2,  
  three = 3,  
};
В отличии от массивов элементы таблицы индексируются переменными. А значением может быть абсолютно любой тип данных Lua, да и вообще всё что угодно:
t = {  
  color = "black",  
  number = 1.2,  
  param = true,  
};
Как уже было сказано выше, значением таблиц могут быть и числа, и булевые значения (true, false), и функции, и строки, и таблицы, и массивы, и даже nil.
Для отделения типов данных друг от друга будет удобно использовать точку с запятой вместо обычной запятой:
t = {  
  number1 = 1,  
  number2 = 2;  
  str = "string";  
  t2 = {  
      n = 4;  
      n1 = true;  
  };  
  m = {"one","two","three",};  
  jj = false,  
  gg = true;  
  wtf = nil;  
};

14.2.1. Обращение к элементам таблицы.

уже было похожая тема 14.1.2
Почти всегда обращение выглядит так:
l = {  
  one = 1,  
  two = 2,  
  three = 3,  
};  
print(l.one); -- 1  
print(l.two); -- 2  
print(l.three); -- 3
Когда в таблице таблица, обращение происходит так:
t = {  
  t1 = {  
    one = 1,  
    two = 2,  
    three = 3,  
  },  
};  
print(t.t1.one); -- 1
Когда в таблице массив, обращаться можно так:
t = {  
  t1 = {"one","two","three",},  
};  
print(t.t1[1]); -- "one"
Ну и понятно, что если в таблице есть массив, внутри которого массив, обращаться можно так:
t = {  
  t1 = {{"one","half",},"two","three",},  
};  
print(t.t1[1][2]); -- "half"
Для таблиц понятия «длина» как такового нет, так что использовать соответствующего оператора для таблиц нельзя.

14.2.2. Цикл обхода элементов таблицы.

уже была похожая тема 14.1.4
Что это — обьяснять не надо. Для обхода элементов таблицы используется pairs, вместо ipairs. И обычно вместо переменной i (index) используется k (key) (но можно, естественно, писать свои названия).
Синтаксис:
for k,v in pairs(...) do  
  ...  
end
Пример:
Есть таблица со списком оружия и его уроном в процентах. Надо присвоить всему оружия нулевой урон:
Weapons = {  
  Knife = 20,  
  Pistol = 40,  
  ShotGun = 80,  
  AssaultRifle = 85,  
  SniperRifle = 95,  
  MiniGun = 100,  
  RocketLauncher = 110,  
};  

for k,_ in pairs(Weapons) do  
  local w = Weapons;  
  w[k] = 0;  
end
или можно использовать решетку( см спец-функции) вместо pairs. В варкрафте часто именно ее использую. # показывает сколько заполнена таблица
for i=a, #table do

end
пример перебор максимальной атаки
attack = {}
attack[1]=28
attack[2]=45
attack[3]=55
for i=a, #attack do
    local max_attack = 0
    if attack[i] > max_attack then
        max_attack = attack[i]
    end    
end

14.2.3. Функции в таблицах.

Сразу создать функцию в таблице, похоже нельзя. Но если такое необходимо, это можно сделать следующим образом:
tab = {};

function tab:func(a,b)
  print(a+b);
end

tab:func(2,2); -- 4

14.3. Функции для работы с таблицами.

Все они начинаются с «table.» (от англ. «table» — ‘таблица’). Вот все они:
table.concat (обертка в одну строку)
table.concat(table [, sep [, i [, j]]]):
Задан массив, в котором все элементы — строки или числа, возвращает table[i]..sep..table[i+1] ··· sep..table[j]. Значение по умолчанию для sep — пустая строка, значение по умолчанию для i — 1, а для j — длина таблицы.
Если i больше j, функция возвращает пустую строку.
table.insert (вставить/добавить в таблицу)
table.insert(table, [pos,] value):
Вставляет элемент value в позицию pos в table, сдвигая вверх остальные элементы. Значение по умолчанию для pos равно n+1, где n — длина таблицы, т. о., вызов table.insert(t,x) добавляет x в конец таблицы t.
Только для массивов.
Пример:
m = {"one","two","three",};  
table.insert(m,"four");

print(m[4]); -- four
table.maxn / table.getn (длина таблицы)
table.maxn(table):
То же, что и table.getn и #.
table.remove(удалить из таблицы)
table.remove(table [, pos]):
Удаляет из table элемент в позиции pos, сдвигая вниз остальные элементы, если это необходимо. Возвращает значение удаленного элемента. Значение по умолчанию для pos — n, где n — длина таблицы, т. о.,
вызов table.remove(t) удаляет последний элементы таблицы t. (Примечание: использование insert-remove со значениями по умолчанию позволяет работать с таблицей как со стандартным LIFO – стеком) Только для массивов.
Пример:
m = {"one","two","three","four"};  
table.remove(m);  

print(m[4]); -- nil
table.sort (сортировка)
table.sort(table [, comp]):
Сортирует элементы таблицы в заданном порядке внутри таблицы, начиная с table[1] и заканчивая table[n], где n — длина таблицы. Если параметр comp задан, то он должен быть функцией,
которая для двух получаемых параметров возвращает true, если первый из них меньше второго (т. о., not comp(a[i+1],a[i]) будет верно для любого i давать true после окончания сортировки).
Если comp не задан, то вместо него будет использован стандартынй оператор Lua «<».
Алгоритм сортировки не стабилен; в том смысле, что равные элементы могут быть переставлены в процессе сортировки.
Также ещё существуют такие полезные функции для массивов:
next
next():
Возвращает следующей индекс массива после указанного и соответствующее ему значение.
Пример:
m = {"one","two","three","four","five"};

print(next(m)); -- 1 one  
print(next(m,1)); -- 2 two  
print(next(m,table.getn(m))); -- nil
unpack
unpack():
Возвращает либо все значения массива, либо от одного индекса и до другого.
Пример:
m = {"one","two","three","four","five"};  

print(unpack(m)); -- one two three four five  
print(unpack(m,2,3)); -- two three  
print(unpack(m,1,4)); -- one two three four
rawget
rawget():
Возвращает значение указанного индекса массива.
Пример:
m = {"one","two","three","four","five"};

print(rawget(m,3)); -- three  
print(rawget(m,table.getn(m))); -- five
Вместо этого можно просто написать так:
print(m[3]); -- three  
print(table.getn(m)); -- five
rawset():
Присваивает указанному индексу массива новое указанное значение.
m = {"one","two","three","four","five"};

rawset(m,1,"oneone");  

print(m[1]); -- oneone
Вместо этого можно просто написать так:
m[1] = "oneone"  

print(m[1]); -- oneone

14.4. Примеры для работы с таблицами.

Довольно часто приходится прогонять по ячейкам таблицы циклом, сверяя все таблицу.
Как правило, нумерация данных таблицы начинается не с нуля, как все привыкли, а с 1. И вся таблица не должна быть пустой nil, иначе, не будет работать логика.
#table возвращает не размер таблицы, а индекс первого nil начиная с 1, 2, 3 ...
Пример 1,2,3,4,nil,6,7,8 => #table будет равен 5
table.remove и table.insert не работают, если в таблице есть пустая строчка nil. Пример 1,2,nil,4,3,6 вы не можете добавить в 7 ячейку с помощью table.insert, тк выдаст дебаггер ошибку. Также и удалить. Все ячейки должны быть забиты данными.
Ниже пример с арифметическим циулом, когда можно обойти все ячейки. Это работает в том случае, если нет пустых ячеек. Иначе, #table не выдал бы правильно размер таблицы
for a=1, #table do
    -что-то делаете с ячейкой таблицы table[a] 
end
Для обхода хеш таблицы используется pairs()
это очень актуально, тк часто в качестве ключа в вакрафте используется handle, type. И данные могут не нумероваться очередно. Пример, допустим, есть юнит, у него записан список итемов, то есть хэндлов, на которых записан: кулдаун, иконка, заряды итд. Иногда возникала проблема обратиться к записанному списку. пока не узнал об pairs.
local units = {}
local count = 0

units[256]="Один"
units[456]="Два"

for k, v in pairs(units) do
    print(k, v)
    count = count + 1
end
print('count: '..count)
как удалить значение? Мы не сможем же пользоваться всегда table.remove правильно? достаточно обнулить ячейку, и тогда цикл pairs() не будет обходить nil.
unit[456]=nil
В этом разделе много было повторов, но надеюсь новичкам поможет прояснить.

`
ОЖИДАНИЕ РЕКЛАМЫ...
0
17
2 года назад
0
Зачем в конце строки везде писать ;
0
27
2 года назад
Отредактирован MpW
0
Зачем в конце строки везде писать ;
Это так автор одной статьи писал. Но лично меня это не смутило. Но показали, что это не так обязательно точку с запятанной ставить. Больше для красоты. Один раз писал в строку несколько действии через точку с запятой, чтобы уменьшить код, и визуально отделить два действия, пробел не помогал иногда
Думаю, нужен еще пример
if a>3 then
  c = 1
else
  c = 0
end
Как записать все в одну строку? C+ это есть. В луа как то
c=a>3 and 1 or 0

-- Получение знака параметра -- Возвращает -1,0,1 (интересная механика, возвращает bool, но получаем на выходе int) 
function math.sign(value) 
return value < 0 and -1 or value > 0 and 1 or 0 end
По этому принципу получается эквивалент:
if value < 0 then
  return -1
elseif value >0 then
  return 0
end
Чтобы оставить комментарий, пожалуйста, войдите на сайт.