Thread Tools Display Modes
08-15-05, 08:07 AM   #1
Garoun
A Fallenroot Satyr
 
Garoun's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2005
Posts: 25
Table/Array Variable appending

Been looking around the lua manuals and can't seem to find the answer to this question, more then likely I'm just blind and can't see it. Basically I was curious if/how you append more data to an already defined array/table with values present.

Code:
Some_Variable = {
[1] = {abcd},
[4] = {efgh}
};

--[[ Appending data ]]
Data_To_Append = {
[6] = {ijkl},
[3] = {mnop}
};
At the most basic level I'd like to take Data_To_Append and add it to the end of Some_Variable.

P.S. In lua are the variables of this nature considered 'Arrays' or 'Tables'?
  Reply With Quote
08-15-05, 09:53 AM   #2
Gello
A Molten Giant
AddOn Author - Click to view addons
Join Date: Jan 2005
Posts: 521
For simple tables you could do:

Some_Variable = {
[1] = "abcd",
[4] = "efgh"
};

--[[ Appending data ]]
Data_To_Append = {
[6] = "ijkl",
[3] = "mnop"
};

for i in Data_To_Append do
Some_Variable[i] = Data_To_Append[i]
end

When they get more complicated, there are recursive functions around but those are beyond me so I brute force them:

Some_Variable = {
[1] = { a=1, b=2, c=3, d=4 },
[4] = { e=2, f=3, g=4, h=5 }
};

--[[ Appending data ]]
Data_To_Append = {
[6] = { i=3, j=4, k=5, l=6 },
[3] = { m=4, n=5, o=6, p=7 }
};

for i in Data_To_Append do
Some_Variable[i] = {}
for j in Data_To_Append[i] do
Some_Variable[i][j] = Data_To_Append[i][j]
end
end
--

Not entirely sure the difference between an array and a table, but tables can be very freeform in lua.
  Reply With Quote
08-15-05, 10:15 AM   #3
Garoun
A Fallenroot Satyr
 
Garoun's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2005
Posts: 25
Thanks Gello that's what I was looking for

Needed it to continue the code I'm working on for making the custom config in SCT work on a per class basis. Without the combining code I couldn't include the the 'Global' array in addition to my class based ones.

Much appreciated!
  Reply With Quote
08-16-05, 06:56 AM   #4
Littlejohn
A Warpwood Thunder Caller
AddOn Author - Click to view addons
Join Date: Jun 2005
Posts: 90
Originally Posted by Garoun
P.S. In lua are the variables of this nature considered 'Arrays' or 'Tables'?
Lua doesn't have arrays -- it's tables all the way down.

There is a special table property named "n" though that the pseudo-array features use as the last index. The functions table.insert, table.remove, and table.getn won't work right without "n" defined. If you want array behavior, you should use these functions to build your table.

If it's awkward to build your table that way, you can call table.setn to change the last index. Be careful doing that though because Lua doesn't care if the table contents and "n" are consistent (you could over-write data in the table).

Here are tables acting like arrays:
Code:
-- no keys, so Lua automatically uses 1,2,3 for keys and getn=3
t = { 'a', 'b', 'c' }

table.insert(t, 'd')

for i=1,table.getn(t) do
  print(t[i])
end

-- if you give any keys, you should give all the keys, otherwise Lua
-- will use keys starting with 1 and those probably won't fit in with
-- the keys you use. getn will be screwed up too.
t = { [5] = 'a', [10] = 'b', [15] = 'c' }

-- now set "n" to the last index (not the table size!)
table.setn(t, 15)

for i=1,table.getn(t) do
  print(t[i])
end

-- the following is a "normal" table loop and hits only the defined
-- table entries in a random order.
for k,v in t do
  print(k,v)
end
  Reply With Quote
08-16-05, 08:20 AM   #5
Garoun
A Fallenroot Satyr
 
Garoun's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2005
Posts: 25
That's exactly what I was wondering wasn't sure of the proper name for those tables since I'm so use to arrays for storing multiple vaules. Maybe you know the answer to these questions about optimization.

1) Is the entire lua code loaded into memory for execution at load or is it read a line at a time. My assumption is the entire code is loaded into memory at start.

2) Which (if any) would be more 'resource' friendly and optimal in WoWAPI lua: (not written in 'true' code form)
Code:
Option 1:
Global_options = {'a','b','c'};
Warrior_options = {'d','e','f'};
Rogue_options = {'g','h','i'};

if (class==Warrior) then append Warrior_options to Global_options
else if (class==Rogue) then append Rogue_options to Global_options
Code:
Option 2:
Global_options = {'a','b','c'};

if (class==Warrior) then {
Warrior_options = {'d','e','f'};
append Warrior_options to Global_options
}
else if (class==Rogue) then {
Rogue_options = {'g','h','i'};
append Rogue_options to Global_options
}
I ask because I'm not completely sure how Lua loads it's code. The second would seem more efficient to me since it would then only have to fill the Global table and the Class table it needs to use for that run. The first option would be easier for maintenance and fast updating of new options.

A 3rd option if possible would be to create all needed tables at the beginning and then do something like, not sure if you can concat variables/strings like an Eval() in js that then act as another variable:

Code:
class = <playerclass>

Global_options = {'a','b','c'};
Warrior_options = {'d','e','f'};
Rogue_options = {'g','h','i'};

append class .. '_options' to Global_options;
The third version seems to cut down on overall code but has the issue of still defining all those tables in memory to begin but removes all the redundant logic for a per class basis.

Appologize for all the questions but thank you for the answers you've both given me so far. It's helped a lot for understanding how the language treats this kind of data; however basic it may be.
  Reply With Quote
08-16-05, 09:07 AM   #6
Littlejohn
A Warpwood Thunder Caller
AddOn Author - Click to view addons
Join Date: Jun 2005
Posts: 90
That's a complicated question... I'm going to answer it in three separate posts. A warning though: each post is gonna get tougher to understand!

Lua and WoW process code a whole file at a time. A file is first read into memory, then converted to internal "bytecode" form, and finally executed. Variable assignment happens in the execution phase, so it's generally more efficient to delay initialization until you are sure you will need the variable. Compare these two examples:
Code:
function f1()
    local x
    if 1 == 0 then
	x = { 'a', 'b', 'c' }
	print(x.getn())
    end
end

function f2()
    local x = { 'a', 'b', 'c' }
    if 1 == 0 then
	print(x.getn())
    end
end
f2() runs 6 times slower than f1() because f2() always creates, assigns and destroys a table even though it doesn't use it.

The flip side of this is that you want your code to be clean and simple so you (and others) can understand it easily. People often expect global variables to be initialized at the top of the file, so even if you can make things a little faster by delaying initialization until necessary, it's probably not worth it because it will confuse people.

You want to go with option 1 and initialize the variables all at once:
Code:
Global_options = {'a','b','c'};
Warrior_options = {'d','e','f'};
Rogue_options = {'g','h','i'};
  Reply With Quote
08-16-05, 09:16 AM   #7
Garoun
A Fallenroot Satyr
 
Garoun's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2005
Posts: 25
Ok with you so far
  Reply With Quote
08-16-05, 09:44 AM   #8
Littlejohn
A Warpwood Thunder Caller
AddOn Author - Click to view addons
Join Date: Jun 2005
Posts: 90
The cascaded "if" is really common in WoW code. Most event handlers have a huge chain of "if ... else if ... else if ...". Lua runs these surprisingly fast, but your idea of using a table to avoid the "if" statement is really good. This kind of code is much easier to maintain and improve -- it's more object oriented. Here's what you wrote:
Code:
class = <playerclass>

Global_options = {'a','b','c'};
Warrior_options = {'d','e','f'};
Rogue_options = {'g','h','i'};

append class .. '_options' to Global_options;
That's pretty close to how it would be in Lua:
Code:
local _, class = UnitClass('player')

Global_options = {'a','b','c'}
WARRIOR_options = {'d','e','f'}
ROGUE_options = {'g','h','i'}

for k,v in getglobal(class .. '_options') do
    table.insert(Global_options, v)
end
I'm using the second class name from UnitClass() because it should work in any language version of WoW. (You won't have to worry about translating this if a German player for example wants to use your mod.)

There's no real reason to use global variables for each class though -- and there are good reasons not to: (1) you have to use a special function getglobal() to fetch them, and (2) you've got more global variables to worry about module conflicts. I think this is a little bit cleaner:
Code:
local _, class = UnitClass('player')

Global_options = {'a','b','c'}
Class_options = {
    WARRIOR = {'d','e','f'},
    ROGUE = {'g','h','i'}
}

for k,v in Class_options[class] do
    table.insert(Global_options, v)
end
Don't forget that if you use tables instead of pseudo-arrays that you'll have to change the append logic:
Code:
local _, class = UnitClass('player')

Global_options = {a = 1, b = 2, c = 3}
Class_options = {
    WARRIOR = {d = 'foo', e = 5, f = 6},
    ROGUE = {g = 'bar', h = 10, i = 100}
}

for k,v in Class_options[class] do
    Global_options[k] = v
end
  Reply With Quote
08-16-05, 10:01 AM   #9
Garoun
A Fallenroot Satyr
 
Garoun's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2005
Posts: 25
Great idea on using the localization form of the UnitClass, as you mentioned, saves the need to create localized versions of the same code in this situation.

Originally Posted by Littlejohn

There's no real reason to use global variables for each class though -- and there are good reasons not to: (1) you have to use a special function getglobal() to fetch them, and (2) you've got more global variables to worry about module conflicts. I think this is a little bit cleaner:
Code:
local _, class = UnitClass('player')

Global_options = {'a','b','c'}
Class_options = {
    WARRIOR = {'d','e','f'},
    ROGUE = {'g','h','i'}
}

for k,v in Class_options[class] do
    table.insert(Global_options, v)
end
Ok, still with you and I can see how the psuedo-tables will work and actually think that would be cleaner and easier to maintain for users. I'll have to look at Grayhoof's code in SCT for his custom configs to see if there is a way to work it in without required changes to the main SCT.lua otherwise I'll just have to use tables like he currently does and go with it.


*Note for self when I get home - thinking out loud*
Possibly use the code for the pseudo table to generate the combined global/class variable then, even though kinda redundant, write the combined into the table variable used by SCT. -ponder-

Last edited by Garoun : 08-16-05 at 10:03 AM.
  Reply With Quote
08-16-05, 10:22 AM   #10
Littlejohn
A Warpwood Thunder Caller
AddOn Author - Click to view addons
Join Date: Jun 2005
Posts: 90
And now the Lua tour de force: metatables.

Why copy tables at all? The copy wastes memory (especially if you have large read-only data sets). Even worse, once you copy a table, any changes made to the original won't show up in the copy, so you have to manually code the update logic. This can get really hairy if you allow people to over-ride options after the copy.

Lua is built on tables so it has a fancy (and fast!) way to inherit values from one table to another without making a copy: metatable.__index.

I'm not going to talk about metatables much. Metatables are a little bit like object classes in Java or Python. Setting a table's metatable basically changes the class of the table from "normal Lua table" to your custom class.

The following code changes the "Player_options" table so it will inherit all the values from the player's Class_options table.
Code:
local _, class = UnitClass('player')

Player_options = {a = 1, b = 2, c = 3}
Class_options = {
    WARRIOR = {d = 'foo', e = 5, f = 6},
    ROGUE = {d = 'bar', h = 10, i = 100}
}

setmetatable(Player_options, { __index = Class_options[class] })

print('a', Player_options.a) -- get 'a' from Player_options
print('d', Player_options.d) -- get 'd' from Class_options.WARRIOR

-- you can dynamically change the metatable. if the metatable
-- is shared among several tables, the change immediately affects
-- all of them.

getmetatable(Player_options)['__index'] = Class_options.ROGUE

print('a', Player_options.a) -- get 'a' from Player_options
print('d', Player_options.d) -- get 'd' from Class_options.ROGUE
You can assign values to Player_options fields just like a normal Lua table. Since I've only defined the __index behavior, all assignments go directly into the Player_options table. The inherited Class_options table is never modified. I think this is a nice feature: Player_options over-ride inherited options and you can update Class_options without worrying if someone changed one of the inherited options.
  Reply With Quote
11-04-05, 09:19 PM   #11
WinHack
A Deviate Faerie Dragon
Join Date: Oct 2005
Posts: 11
I have a much more basic question on tables/arrays. I understand that there are no arrays per se in Lua, and that Tables are the way to go here. I know how to create them when you have all the data ( x = { "a", "b", "c" } ).

If I wanted to create and append tables dynamically, how do I do that? Is there a very simple tutorial example somewhere that shows basic array-like table manipulation? The documentation at www.lua.org is not very clear to me, and I was hoping there was a better example somewhere that I could look at.

Basically, what I am trying to do is a simple "If (some_case_is_true) then add 'this' to the array" (yeah, I know--tables. I'm just an old BASIC hack at heart). Basically, I don't know how big the array might grow to be (since that depends on the situation at the moment), so I need to be able to keep growing it as more data applies (between 1 and 15 rows, max). The pseudo code would look something along the simplified lines of:
counter = 1;
for x = 1, limit do
if (condition == met) then
array1[counter] = "something";
counter = counter + 1;
end
end

The questions I come up with are:
1) Are there specific commands I need to use to get a table to behave as I am intending above?
2) Are tables 0-based or 1-based?
3) If it really is a table, would a table named Table[x][y][z] mean all those elements are on the same row (three columns on the row), or are they behaving like dimensions (for the combination of a specific [x] and [y], there are a theoretically unlimited number of [z]s).

I kind of understand the discussion above, but since I've pretty much worked with programming using arrays (except for SQL/database programming), I'm not sure I'm understanding how they are implemented here in Lua. Any simple pointers or examples would be most useful.
__________________
In theory, practice and theory are the same thing. In practice, they are not.
  Reply With Quote
11-07-05, 10:57 PM   #12
WinHack
A Deviate Faerie Dragon
Join Date: Oct 2005
Posts: 11
Never mind...found one. http://lua-users.org/wiki/TablesTutorial
__________________
In theory, practice and theory are the same thing. In practice, they are not.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Table/Array Variable appending


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off