• 0

    posted a message on Table sorting question.
    Quote from tekkub »

    Your method creates a closure every time it's called, that wastes memory and creates garbage churn. Make your function once and reuse it.

    His method creates a new closure but it does not create garbage. The closure is on the stack; when table.sort returns the closure is automatically erased. Nothing is allocated on the heap.
    Posted in: Lua Code Discussion
  • 0

    posted a message on Table sorting question.
    Quote from Marroc »

    Thank you :P

    Helped a ton. It's kind of an odd switch from languages like C, Java, and Scheme to LUA >_<

    Just FYI, Lua is not an acronym :P (It means "moon" in Portuguese)
    Posted in: Lua Code Discussion
  • 0

    posted a message on How to make your AddOn Clean and Modularized
    Some of the underscores were taken out of the code snippets... I dunno why. Copy/Pasting seems to preserve them however.
    Posted in: Lua Code Discussion
  • 0

    posted a message on How to make your AddOn Clean and Modularized
    So you're making an AddOn. You've probably looked at plenty of AddOns' code and now think you're ready to make your own. Have you ever done:

    /script for i in pairs(_G) do DEFAULT_CHAT_FRAME:AddMessage(i) end

    ? Probably not. I wouldn't recommend it either because you'll probably lock up wow and get disconnected. The reason for this is the global environment is very cluttered - full of functions for the WoW API and full other useful global variables. It doesn't help the situation that most addon users put their own functions and global variables in this environment (especially localized strings). One AddOn user I know has upwards 31774 global variables! So how do you help yourself keep your code separate from others who might accidentally overwrite your functions or variables with a similar name? Modularize your code.

    At the top of you .lua file place:

    local ADDONNAME = ADDONNAME or { };
    local _G = getfenv(0); -- Gets the current global environment
    -- Now for the fun stuff:
    _G[ADDONNAME._NAME] = ADDONNAME; -- Adds the module table to
                  -- the global environment.
    setmetatable(ADDONNAME, {__index = _G}); -- The global environment is now checked if a key is not found in ADDONNAME
    setfenv(1, ADDONNAME); -- The real magic
    function print(msg)

    What have I done here? The environment you are in is no longer the global environment, it is now the table: ADDONNAME. Everything you do globally is put in there - all function definitions, all global variables. From within WoW you can try this:

    /run ADDONNAME.print("hi")

    You should see "hi" printed to the chat frame. Why is this so powerful? First you don't clutter the global namespace with your functions and variables (as said earlier). Second, you don't need to prefix any of your functions or variables because you're afraid they might be overwritten by someone else (especially true with a print function). Third, you can safely place all your localized variables inside your addon table, with clear names without prefixes.

    Howto add localized variables:

    file ADDONNAME.en.lua:
    local ADDONNAME = ADDONNAME or { };
    local _G = getfenv(0);
    setmetatable(ADDONNAME, {__index = _G});
    setfenv(1, ADDONNAME);
    HELLO = "Hello";
    -- ... other localized strings

    Typically you should run these localization files before any other code so these strings are immediately available. The first line is especially important for this reason:

    local ADDONNAME = ADDONNAME or { }

    If the environment is already available, then we adopt it. That way you can place all the strings inside the table, and later when your main .lua file is run, it adopts this table instead of making a new one.

    So what else can you do with this?

    You can see in my AddOn:


    that I have 8 (currently) submodules in addition to my main .lua file (Evocate, Release, etc.). I add their environments to my AddOn's table Spellcraft. That way, my main module serves as a library for all my submodules that provide actual functionality to the user. In that way, my code is modularized (features that are separate, are kept separate from each other), but they can use the same basic AddOn functions such as print() or error() which I define for all my submodules. Many other extremely interesting constructs can be created using these ideas as you can see inside my AddOn. I use one frame for all event registration, all submodules are required to have a Notify Function so I can notify them when an event they register for (using my interface) occurs. It is very simple yet powerful.
    Posted in: Lua Code Discussion
  • 0

    posted a message on ... having trouble using events with arguments?
    You want to do it like this to avoid table lookups (arg1 is in the global environ)

    function Threshold:UNIT_HEALTH(unit)
      if unit == "player" and UnitHealth("player") < 90 then
         self:Print("Oh shit!")
    Posted in: Lua Code Discussion
  • 0

    posted a message on Table sorting question.
    Quote from Marroc »

    [code]-- Returns the final item list string function ItemExport:FormatItemList() local itemList = "" table.sort(data, function(a, b) return a[1] < b[1] end) for i,v in ipairs(data) do itemList = itemList .. self.db.profile.prefix .. v[1] .. self.db.profile.suffix .. " x" .. v[3] .. "\n" end return itemList end -- Adds the given item to the data table function ItemExport:AddItem(itemname, quality, itemcount) local found = false for i,v in ipairs(data) do if itemname == v[1] then v[3] = v[3] + itemcount found = true return end end if not found then table.insert(data, {itemname, quality, itemcount}) end end[/code]

    The first is generating a string based on the item information (works just fine) The second one is the function being called to add the information into the data table (also working just fine).

    The only line that I can't get to work as expected is the "table.sort(data, function(a, b) return a[1] < b[1] end)" That exact phrase does what you'd expect. Take element one from array a, and compare it to element 1 of array b. Changing the element number to anything else causes it to tell me that the array is nil (which it shouldn't be).

    To answer you question the table is laid out as follows:

    {index, {itemname, quality, itemcount}}

    EVERYTHING else works perfectly fine.

    Running an ipairs over the table works exactly as expected for example (i being the index, and v[n] being the value of the current index's array at element n).

    I don't understand your use of index. You seem to be a little lost with what you're doing so let me explain how I would do it, perhaps along the way you will understand. If you have a question, just ask.

    Here is how I would have my items table:
    [code]Items = {{itemname, quality, itemcount}, {itemname2, quality2, itemcount2} };[/code]

    The table will be indexed by a number 1-N.

    If this table gets extremely large, you would be well served by having another table that is a reverse lookup table. It serves to find entries for you quickly so you don't have to iterate over them, as you did in your AddItem method. The reverse lookup table would look like this:

    [code]ItemsR = {itemname = index, itemname2 = index2}[/code]
    Thus you can find the index of an itemname by:
    [code]Items[ItemsR[itemname]] --The table {itemname, quality, itemcount} Items[ItemsR[itemname]][3] = itemcount + Items[ItemsR[itemname]][3]; -- For your AddItem function, add the itemcount[/code]

    When you add an item to your Items table you will need to add it to your reverse lookup table.

    You should understand that when you have a table constructor such as {"dog", "cat", itemname, nil, "whatever", 5}
    This puts those values in an array such that "dog" is indexed by 1 and 5 is indexed by 6. BUT, when you use ipairs to iterate over the function, it stops at nil. nil servers as a sentinel as the end of a table. If you need to iterate over all the elements in a table, use pairs(). Keep in mind, that by having nil inside the array, you lose a lot of the functionality of your array (speed by using ipairs among other things). You should use placeholder values if you don't have a value to give (e.g. -1).

    Onto your problem with table.sort:

    if Items is defined as

    [code]Items = {{itemname, quality, itemcount}, {itemname2, quality2, itemcount2} };[/code]

    Then you can sort it by:

    [code]table.sort(Items, function(a, b) return a[2] < b[2] end); -- Will sort by quality[/code]

    If you use nil as a placeholder for quality or itemcount this will error! You must use values that can be compared. If you must use nil, then you can use a construct such as:

    [code]table.sort(Items, function(a, b) return (a[2] or 0) < (b[2] or 0) end); -- Will sort by quality, even with nil[/code]

    if a[2] is nil then 0 is used instead. The parenthesis are necessary because the '<' operator has higher precedence than 'or'. Thus without correct parenthesis,

    [code][code]table.sort(Items, function(a, b) return a[2] or 0 < b[2] or 0 end);[/code]

    will be seen as

    [code][code]table.sort(Items, function(a, b) return a[2] or (0 < b[2]) or 0 end);[/code]

    which will always be true to matter what because 0 is considered true in logic evaluation.

    Posted in: Lua Code Discussion
  • To post a comment, please or register a new account.