Thread Tools Display Modes
03-15-12, 05:02 AM   #1
Animor
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Mar 2011
Posts: 136
3 LUA questions (array, vars scope, mob health)

Hello,

I have some LUA related questions I hope someone here will be able to answer.

1) What is the best way to search an array?
Code:
for _, value in pairs(myArray) do
  -- use value
end
or
Code:
for i = 1, #myArray do
   -- use myArray[i]
end
or perhaps another way?

2) Let's say I have this code:
Code:
function xxx ()
   local counter = ...
   
   for counter = 1, 3, 1 do
      print(counter)
   end

   -- use counter
end
Is "counter" of the for loop local only for the for loop scope? Or will the loop alter the "counter" declared at the beginning of the function?

3) I want to track boss health. for that I use "boss1" .. "boss4" which relate to the 4 boss frames on the right side of the screen. for example:
Code:
UnitHealth("boss1")
The problem is that at a certain phase some adds might fill those boss frames and "push out" the original boss. This happens for example on Yor'sahj, when 4 Globules appear and "take" all 4 boss frames. How can I still get Yor'sahj health in such scenario?
Is there a way to get unit health through the unit name for example? Any other solution?

Thanks in advance!
  Reply With Quote
03-15-12, 06:03 AM   #2
haste
Featured Artist
 
haste's Avatar
Premium Member
Featured
Join Date: Dec 2005
Posts: 1,027
1) Depends on the content of your array. If it is an array with unsorted data, then the second solution should be slightly better, but not by much. If it's sorted then doing a binary search would probably be better.

2) The first counter is local to the scope of xxx(), while the for loops counter is only local to the for loop. quick example:
Lua Code:
  1. Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
  2. > do
  3. >> local c = 0
  4. >> for c=1, 3 do print(c) end
  5. >> print(c)
  6. >> end
  7. 1
  8. 2
  9. 3
  10. 0

3) You can do it by tracking changes to the HP through the combat log or through someones target. No directly easy way to track such when there's no valid unit id to track.
__________________
「貴方は1人じゃないよ」
  Reply With Quote
03-15-12, 07:11 AM   #3
Animor
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Mar 2011
Posts: 136
Thanks!

Can you elaborate a bit about the impact of unsorted array on those two search methods?
btw, I also saw usage of "ipairs" instead of "pairs". Which one is better for going over array elements?

Thanks.
  Reply With Quote
03-15-12, 10:53 AM   #4
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,860
It means by either having numerical keys or a dictionary-style lookup table. (ie, whether you name the keys or leave them as numbers)
__________________
"You'd be surprised how many people violate this simple principle every day of their lives and try to fit square pegs into round holes, ignoring the clear reality that Things Are As They Are." -Benjamin Hoff, The Tao of Pooh

  Reply With Quote
03-16-12, 02:54 AM   #5
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Array = indexed table = table where the keys are sequential integers beginning with 1.

Dictionary = hash table = table where the keys are anything else.

Unlike some programming languages, Lua uses the same data structure -- the table -- for both paradigms.

This is an array:
Code:
local t = {
	"apple",
	"banana",
	"cherry",
}
This is also an array, and is identical to the above:
Code:
local t = {
	[1] = "apple",
	[2] = "banana",
	[3] = "cherry",
}
The ipairs function will operate identically on both tables:
Code:
for i, v in ipairs(t) do print(i, v) end
1 apple
2 banana
3 cherry
However, if the indices are not sequential, like this:
Code:
local t = {
	[1] = "apple",
	[2] = "banana",
	[3] = "cherry",
	[7] = "grape",
	[13] = "mango",
}
Then ipairs will only iterate over the indices that begin at 1 and increment sequentially:
Code:
for i, v in ipairs(t) do print(i, v) end
1 apple
2 banana
3 cherry
Basically, it will act as if the other values aren't in the table.

You can also use pairs on an array, and it will return all the values, including the ones that ipairs skips because they aren't sequential, but it returns them in an undefined order, which basically means random:
Code:
for k, v in ipairs(t) do print(k, v) end
3 cherry
13 mango
7 grape
1 apple
2 banana
You can also mix tables, like this:
Code:
local t = {
	"apple",
	"banana",
	[3] = "cherry",
	[7] = "grape",
	["foo"] = "bar",
	["num"] = 5,
}
In this case, ipairs will still iterate over the sequential indexed values, beginning with 1:
Code:
for i, v in ipairs(t) do print(i, v) end
1 apple
2 banana
3 cherry
... and pairs will iterate over all of the key/value pairs, including the indexed ones (keys are sequential integers counting up from 1), in undefined order:
Code:
for k, v in ipairs(t) do print(k, v) end
foo bar
2 banana
3 cherry
num 5
7 grape
1 apple
If you are using an array, it is slightly faster to avoid ipairs and simply iterate through the table this way:

Code:
for i = 1, #t do print(i, t[i]) end
1 apple
2 banana
3 cherry
... but it's not really significant, so if you feel that ipairs is easier for you as a programmer to read and understand, feel free to use it.

As for which type of table structure you use, that depends on what kind of data you want to store, and how you want to use it. Without knowing that, I can't make any useful suggestions.

Last edited by Phanx : 03-16-12 at 02:57 AM.
  Reply With Quote
03-16-12, 05:24 AM   #6
Animor
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Mar 2011
Posts: 136
Phanx,
Thank you for your thorough explanation!

Why is it faster to avoid ipairs and simply iterate through the array, as you wrote?
  Reply With Quote
03-16-12, 05:53 AM   #7
Ketho
A Pyroguard Emberseer
 
Ketho's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,026
This thread might also be of help
http://forums.wowace.com/showthread.php?t=19541

Originally Posted by Animor View Post
Why is it faster to avoid ipairs and simply iterate through the array, as you wrote?
The first method (iterating through the array), because it is a table lookup only. The second method (ipairs) calls a function which basically does the same table lookup and returns the result.

Originally Posted by Phanx View Post
If you are using an array, it is slightly faster to avoid ipairs and simply iterate through the table this way:

... but it's not really significant, so if you feel that ipairs is easier for you as a programmer to read and understand, feel free to use it.
I totally agree with this

and maybe someone is going to pop up with some benchmarks
  Reply With Quote
03-16-12, 11:39 AM   #8
Mikord
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 61
Originally Posted by Animor View Post
Why is it faster to avoid ipairs and simply iterate through the array, as you wrote?
The simplest answer is that ipairs has overhead associated with it since it is an iterator function. The example using the # operator, on the other hand is, just looping from 1 to the number of elements in the array.


Here are some very basic benchmark results between an empty loop iterating over an array with 10,000,000 entries using ipairs versus the # operator. The code is at the bottom of the post:

Code:
Generating array with 10000000 entries...
Iterating through 10000000 entries with # operator...
elapsed time: 0.09
Iterating through 10000000 entries with ipairs...
elapsed time: 1.34

Lua Code:
  1. local NUM_ITERATIONS = 10000000
  2.  
  3. print(string.format("Generating array with %d entries...", NUM_ITERATIONS))
  4. local array = {}
  5. for i = 1, NUM_ITERATIONS do
  6.     array[i] = i
  7. end
  8.  
  9. print(string.format("Iterating through %d entries with # operator...", #array))
  10. local start = os.clock()
  11. for i = 1, #array do
  12. end
  13. print(string.format("elapsed time: %.2f", os.clock() - start))
  14.  
  15. print(string.format("Iterating through %d entries with ipairs...", #array))
  16. local start = os.clock()
  17. for k, v in ipairs(array) do
  18. end
  19. print(string.format("elapsed time: %.2f", os.clock() - start))

Last edited by Mikord : 03-16-12 at 11:43 AM.
  Reply With Quote
03-16-12, 11:52 AM   #9
Animor
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Mar 2011
Posts: 136
Very interesting, thank you for that benchmark!
  Reply With Quote
03-17-12, 02:22 AM   #10
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Mikord View Post
The simplest answer is that ipairs ... is an ... function.
Fixed. Calling functions is basically the slowest thing you can do in Lua, so you should do it as little as possible. In the same vein, if you ever find yourself writing something like this:

Code:
local function DoAnotherThing()
    -- do another thing here
end

local function DoAThing()
    -- do a thing here
    if something then
        DoAnotherThing()
    end
end

DoAThing()
... ask yourself if the code inside the DoAnotherThing() function will ever actually be called from anywhere else. If the answer is "no", then you should just move that code into the DoAThing() function.
  Reply With Quote
03-17-12, 03:41 AM   #11
Animor
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Mar 2011
Posts: 136
Then why in the wow programming book they wrote that
Code:
table.insert(tbl, "new element")
is better than
Code:
tbl[#tbl + 1] = "new element"
They wrote about the second method:
This is a really tedious and error-prone way to do something relatively simple.
  Reply With Quote
03-17-12, 03:43 AM   #12
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Because it's "tedious and error-prone". The second way is faster, though, and it's what I tend to use.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
03-17-12, 07:42 PM   #13
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
The most efficient code isn't always the most readable code, so if you find a particular construct difficult to read and understand, you might choose to use code that's slightly slower or uses slightly more memory to make the code easier for you to write and maintain.

Generally, the difference between things like:
Code:
for i, v in ipairs(tbl) do
    -- do something with v
end
vs
Code:
for i = 1, #tbl do
    local v = tbl[i]
    -- do something with v
end
or
Code:
table.insert(tbl, "lolcats")
vs
Code:
tbl[#tbl+1] = "lolcats"
is too insignificant to be noticed in actual usage, so you should use whichever is easier for you to work with.

The exception would be if you're using it somewhere where speed really matters, like inside an OnUpdate script or in response to an event that fires very frequently like COMBAT_LOG_EVENT_UNFILTERED; in these kinds of places, you should always use the faster code.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » 3 LUA questions (array, vars scope, mob health)

Thread Tools
Display Modes

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