Honestly, why do you need it? I've never felt need for such a function, and I've written a shitton and a half of mods. I think you're approaching your problem the wrong way. What are you trying to do?
There's really only one good reason to use unpack() and that's to return a variable list of values from a function.
-- This function returns 1, 2, 3 ... n
function somefunction(n)
local ret = {} -- I know, shut up!
for i = 1, n do
table.insert(ret, i)
end
return unpack(ret)
end
And since you can only use the values (keys are thrown out), you might as well redo the table (or just use table.insert() to begin with) to be passed to unpack()
function indexifyTable(t)
local table = {}
for _, v in pairs(t) do
table.insert(table, v)
end
return table
end
t = {
'a',
['a'] = 1,
}
-- arg1 = 'a' and arg2 = '1'
local arg1, arg2 = unpack(indexifyTable(t))
"Wrong" == all these examples egingell keeps giving with tables. I know he knows better, but he needs to stop telling people that don't know better to do these things. Stop bad programming before it starts :P
There may be valid reasons to need this, but I can't think of them. That's why I want to know what he's trying to do, there's probably a better way to do it in lua than what he's thinking. It's kinda obvious that he's used to another language where he needs these things, and he needs some help learning the weird lil' ins and outs of lua. So maybe "wrong" is a bad word, we're just looking for "better" ways to do whatever he's after.
There's nothing wrong with a little garbage. It's not like we're back in the dark ages when 64 megs of RAM was a lot.
Anyway, perhaps this example would be better?
do
local ret = {}
-- This function returns 1, 2, 3 ... n
function somefunction(n)
ret = {}
for i = 1, n do
table.insert(ret, i)
end
return unpack(ret)
end
end
do
local table = {}
function indexifyTable(t)
table = {}
for _, v in pairs(t) do
table.insert(table, v)
end
return table
end
end
local t = {
'a',
['a'] = 1,
}
-- arg1 = 'a' and arg2 = '1'
local arg1, arg2 = unpack(indexifyTable(t))
egingell, you might actually want to look over what you posted. You're still creating a new table every function call, instead of clearing the upvalue table you overwrite it with a new one.
Also, the following function is better for unpacking values in a table that isn't numerically indexed.
function myUnpack(t, state)
local key, value = next(t, state)
if key then
return value, myUnpack(t, key)
end
end
This function will not overflow the stack as tail calls are used. And generates no garbage. You may verify that the variables p, q, r, s below will contain the 4 numbers in the table in any order.
t = {["a"] = 56, ["c"] = 12, ["b"] = 34, ["d"] = 53}
local p, q, r, s = myUnpack(t)
Generally, you don't want to do this because unpacking a table can return an unknown number of return values and you don't know how many variables you need to assign on the left. It is better to just iterate over the table in the first place.
"A little garbage" does hurt when "no garbage" is an option. You should avoid creating garbage when you don't need to, period.
ESPECIALLY when you are giving someone new to the language examples. You show them "{...}" once and they will fucking run with it. Give the best examples you can, not lazy fast examples that generate unneeded garbage. Tables are not the end-all solution to doing things, loops and recursion usually provide much cleaner solutions.
egingell, you might actually want to look over what you posted. You're still creating a new table every function call, instead of clearing the upvalue table you overwrite it with a new one.
How does one go about clearing the upvalue without creating more garbage or CPU time? Sure, I could call a function that iterates through the table and nils out all of its elements, but that, to me, seems a bit unnecessary. I don't know of any other way(s) to do it, other than calling collectgarbage("collect").
Following is an open answer to tekkub gently issued question, so if eye's of you don't care please don't point them towards the next lines unless they promise to come with wise advice and not just to criticize.
History Made Short:
Since the first second I sat in front of the WoW Interface I knew I had a something that can be flexed, chewed and rejoined at will, I fear that could doom me but I think it helped me a lot. I try to do things the best way I can, tend to perfection my self at every second based on observation and feelings.
For my the key binds and the ways to use the UI are the path to make one feel like the Character actions, spell casting and else are like an extension of one self creating a really interesting INTUITIVE bond to those actions. I hate disorder so I started to create a method to solve this problem. I hope you respect the following words: after some design attempts I came to decide that the base of the design should be based on the way my hand works (basically diving it into thumb (spell command), the 3 main fingers(movement) and as last group the little one(support: targeting, modifier keys, etc)). After accomplishing the design and a method to draw designs I started my venture into this scripting world, learning from what AddOns were and how can I use them to solve my problems. Many were really promising but none offered my this small characteristics (at least I didn't found one that accomplish them as needed), for example: to create a self cast button not restricted to Ctrl, Alt or Shift (even if key board is suited this keys for those purposes), create up-down reactive buttons to change action bar pages and not be limited just to Ctrl, Alt and Shift Headers. I tested some and came into the decision to get my hands dirty as WoW seemed a damn good evolving game with a community that empowers many systems to orbit around it. I found that I could create just some small action, spell or macro buttons to complement other addons, but they all have their problems and I am not still really capable of solving them.
I step now to deal with my code, hope to be pulled by righteous judgment. I know it can be greatly improved but there is a ton still to be done and to refine:
First the thing I am working on now is the "ensamblator" of the Action Bars and subparts. This may seem ugly but is more or less how you define a complete Action Bar for the addon:
The concept is like a tree, being the ActionBar as the roots, the Headers the body that pump the life (the awareness of events) to the branches (buttonsGroup) finally reaching the leaves (buttons). Whatever is defined in a group it saves the individual dimensioning and don't restricts to have one (individual definition).
Skipping many parts of the code we came across with this part of the idea:
Get every property from the definition (tree) and set it if needed. Let the User use whatever property he wants to, and if he needs any personal one he should have a way to create it and define how to set it. (I know this has it's limitations).
After the frame creation something like the following comes:
(this is also not finished, it has to sense if propertyData is a table or just a value)
for propertyName, propertyData in pairs(buttonData.Property) do
local t = _QF.GetPropertyValue(button, propertyName, propertyData);
if not (t) then
_QF.SetPropertyValue(button, propertyName, propertyData);
end
end
for propertyName, propertyData in pairs(refTable.Property) do
local t = _QF.GetPropertyValue(button, propertyName, propertyData);
if not (t) then
_QF.SetPropertyValue(button, propertyName, propertyData);
end
end
and here is were the unpack happens to help me, some properties can be "extracted" by just calling them for their names others need additional parameters. Also in the proses of setting them they have variable number of parameters.
Q2: Well here is the _QF.GetPropertyValue and _QF.SetPropertyValue base design, I feel I make a huge mistake here and hope any can help.
(incomplete)
----------------------------------------------------------------------------------------------------
local function print (s)
----------------------------------------------------------------------------------------------------
DEFAULT_CHAT_FRAME:AddMessage(tostring(s));
end
----------------------------------------------------------------------------------------------------
function QGetPropertyValue (object, property, ...)
----------------------------------------------------------------------------------------------------
local oType = object:GetObjectType();
print("Get "..oType);
property = "G_" .. property;
return QProperty[oType][property](object, ...);
end
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
function QSetPropertyValue (object, property, ...)
----------------------------------------------------------------------------------------------------
local oType = object:GetObjectType();
print("Set");
property = "S_" .. property;
return QProperty[oType][property](object, ...);
end
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
--Testing
local button = CreateFrame("CheckButton", "CustomActionButton1", UIParent, "ActionBarButtonTemplate");
button:SetPoint("CENTER");
button:SetAttribute("action", 2);
print ("PART I");
local function pan(...)
local arg1, arg2 = ...;
print(select('#', ...));
--unpack(...) = "uno", "dos", "tres", "cuatro", "cinco"; --I know this was one of my erly mistakes but was testing again.
print(select('#', ...));
local a, b = ...
print ("a = " .. a .. " " .. "b = " .. b);
end
pan("vingawololenga", "asdqweq", "wqwe");
print ("-------------------------------------------------------------");
print ("PART II");
print(QGetPropertyValue (button, "Name"));
QSetPropertyValue (button, "Height", 100);
print ("-------------------------------------------------------------");
print ("PART III");
local function pprint (...)
local a, b, c, d, e = ...;
print("A = " .. a);
print("B = " .. b);
print("C = " .. c);
print("D = " .. d);
print("E = " .. e);
end
pprint (unpack({_a = "A", _b = "B", _c = "C", _d = "D", _e = "E"})); --as spected this causes an error unpack works only for numbered keys.
----------------------------------------------------------------------------------------------------
As you can notice QGetPropertyValue and QSetPropertyValue seek in a table the way to set or get a property, when this 2 functions are called (Q functions) they need that variable list of parameters and in this case is provided by the tree, sometimes this tree is in the form of:
Taking as example backdropcolor definition:
In tree:
FrameName = {
Property = {
BackdropColor = {r = 123, g = 123, b = 123, a = 0},
}
}
so this property is a table, to set it I should call:
for propertyName, propertyData in pairs(buttonData.Property) do
local t = _QF.GetPropertyValue(button, propertyName, propertyData);
if not (t) then
_QF.SetPropertyValue(button, propertyName, propertyData);
end
end
So since the keys in FrameName.Property.BackdropColor aren't numerical unpack fails:
leavingme with 3 options:
1 - Make BackdropColor (Tree)and other complex properties definitions numericaly indexed so loosing flexibility at quering and readability but keeping unpack function intact.
2 - Make every Set Method to work with a table or unpack.
3 - Parse the attributes using a custom unpack function (thought I lost the unpack gem coded in C).
S_BackdropColor = function (o, r, g, b, a) o:SetBackdropColor(r, g, b, a); end;
Please if I wasn't clear in any line ask me to rephrase and edit.
Thanks to everyone.
FF.
function myUnpack(t, state)
local key, value = next(t, state)
if key then
return value, myUnpack(t, key)
end
end
This function will not overflow the stack as tail calls are used. And generates no garbage.
I'm afraid that's not correct. The above does not use tail calls. Only when returning the result of a call directly, will tail calls be used. That is, it must look like this:
return f()
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
Q1: Is there any function twin to unpack arrays or tables with any kind of keys or should I write my own function?.
Thanks in Advance.
FF.
unpack(t) only works on numerically indexed tables.
do what i did, steal the UnpackAndDel() function from RockLib
And since you can only use the values (keys are thrown out), you might as well redo the table (or just use table.insert() to begin with) to be passed to unpack()
{GetGossipAvailableQuests()}
:D
There may be valid reasons to need this, but I can't think of them. That's why I want to know what he's trying to do, there's probably a better way to do it in lua than what he's thinking. It's kinda obvious that he's used to another language where he needs these things, and he needs some help learning the weird lil' ins and outs of lua. So maybe "wrong" is a bad word, we're just looking for "better" ways to do whatever he's after.
Anyway, perhaps this example would be better?
Also, the following function is better for unpacking values in a table that isn't numerically indexed.
This function will not overflow the stack as tail calls are used. And generates no garbage. You may verify that the variables p, q, r, s below will contain the 4 numbers in the table in any order.
t = {["a"] = 56, ["c"] = 12, ["b"] = 34, ["d"] = 53}
local p, q, r, s = myUnpack(t)
Generally, you don't want to do this because unpacking a table can return an unknown number of return values and you don't know how many variables you need to assign on the left. It is better to just iterate over the table in the first place.
ESPECIALLY when you are giving someone new to the language examples. You show them "{...}" once and they will fucking run with it. Give the best examples you can, not lazy fast examples that generate unneeded garbage. Tables are not the end-all solution to doing things, loops and recursion usually provide much cleaner solutions.
yea... take a look at automation... it's horrific :\
FF.
How does one go about clearing the upvalue without creating more garbage or CPU time? Sure, I could call a function that iterates through the table and nils out all of its elements, but that, to me, seems a bit unnecessary. I don't know of any other way(s) to do it, other than calling collectgarbage("collect").
History Made Short:
Since the first second I sat in front of the WoW Interface I knew I had a something that can be flexed, chewed and rejoined at will, I fear that could doom me but I think it helped me a lot. I try to do things the best way I can, tend to perfection my self at every second based on observation and feelings.
For my the key binds and the ways to use the UI are the path to make one feel like the Character actions, spell casting and else are like an extension of one self creating a really interesting INTUITIVE bond to those actions. I hate disorder so I started to create a method to solve this problem. I hope you respect the following words: after some design attempts I came to decide that the base of the design should be based on the way my hand works (basically diving it into thumb (spell command), the 3 main fingers(movement) and as last group the little one(support: targeting, modifier keys, etc)). After accomplishing the design and a method to draw designs I started my venture into this scripting world, learning from what AddOns were and how can I use them to solve my problems. Many were really promising but none offered my this small characteristics (at least I didn't found one that accomplish them as needed), for example: to create a self cast button not restricted to Ctrl, Alt or Shift (even if key board is suited this keys for those purposes), create up-down reactive buttons to change action bar pages and not be limited just to Ctrl, Alt and Shift Headers. I tested some and came into the decision to get my hands dirty as WoW seemed a damn good evolving game with a community that empowers many systems to orbit around it. I found that I could create just some small action, spell or macro buttons to complement other addons, but they all have their problems and I am not still really capable of solving them.
I step now to deal with my code, hope to be pulled by righteous judgment. I know it can be greatly improved but there is a ton still to be done and to refine:
First the thing I am working on now is the "ensamblator" of the Action Bars and subparts. This may seem ugly but is more or less how you define a complete Action Bar for the addon:
Skipping many parts of the code we came across with this part of the idea:
Get every property from the definition (tree) and set it if needed. Let the User use whatever property he wants to, and if he needs any personal one he should have a way to create it and define how to set it. (I know this has it's limitations).
After the frame creation something like the following comes:
and here is were the unpack happens to help me, some properties can be "extracted" by just calling them for their names others need additional parameters. Also in the proses of setting them they have variable number of parameters.
Q2: Well here is the _QF.GetPropertyValue and _QF.SetPropertyValue base design, I feel I make a huge mistake here and hope any can help.
As you can notice QGetPropertyValue and QSetPropertyValue seek in a table the way to set or get a property, when this 2 functions are called (Q functions) they need that variable list of parameters and in this case is provided by the tree, sometimes this tree is in the form of:
Taking as example backdropcolor definition:
so this property is a table, to set it I should call:
but this is being retribed in the loop like:
So since the keys in FrameName.Property.BackdropColor aren't numerical unpack fails:
leavingme with 3 options:
1 - Make BackdropColor (Tree)and other complex properties definitions numericaly indexed so loosing flexibility at quering and readability but keeping unpack function intact.
2 - Make every Set Method to work with a table or unpack.
3 - Parse the attributes using a custom unpack function (thought I lost the unpack gem coded in C).
Please if I wasn't clear in any line ask me to rephrase and edit.
Thanks to everyone.
FF.
I'm afraid that's not correct. The above does not use tail calls. Only when returning the result of a call directly, will tail calls be used. That is, it must look like this: