#!Lua50.exe -- But works better with Lua 5.1, which I used to generate the HTML file... --[[ A non-exhaustive look at table and array capabilities of Lua. Made for educational purpose, and to allow comparison of syntax/features of JavaScript and PHP in the same domain. by Philippe Lhoste http://Phi.Lho.free.fr File/Project history: 1.00 -- 2007/04/17 (PL) -- Creation. ]] --[[ Copyright notice: See the PhiLhoSoftLicence.txt file for details. This file is distributed under the zlib/libpng license. Copyright (c) 2007 Philippe Lhoste / PhiLhoSoft ]] eol = '\n' function DoTest() function StupidFunction(p) return p end testPart = 3 ---- Arrays with numerical index -- Indexes in Lua start at 1 -- Simple definition and putting values in it local simpleArray = {} simpleArray[1] = "GET" simpleArray[2] = "POST" simpleArray[3] = "PUT" simpleArray[4] = "DELETE" -- Pre-definition and initialization local predefinedArray = { nil, nil, nil, nil } -- Clumsy way, no other... table.insert(predefinedArray, "HTTP") table.insert(predefinedArray, "FTP") table.insert(predefinedArray, "NNTP") table.insert(predefinedArray, "IRC") -- We can skip entries. But some functions like table.sort doesn't like that... -- And table.getn will count only up to the first empty entry -- Slots 5 and 6 are empty table.insert(predefinedArray, table.getn(predefinedArray) + 3, "FTPS") -- Definition and initialization at once local initializedArray = { "html", "head", "body", } -- Trailing comma is OK -- Generating an array local generatedArray1 = Split("a:b:i:p:u:random:garbage", ":", 5) local generatedArray2 = Split("hr , br ; p ,span, div", '%s*[;,]%s*') -- Assigning an array local gArray = Split("aa ! bb ! cc ! dd ! ee", " ! ") local aArray = gArray -- aArray gets a reference, the arrays are the same gArray[4] = "gg" aArray[5] = "zz" -- Array as function parameter and return value function ProcessArray(a) a[1] = "ff0" -- Change original array local la = { "1", "2", "3", a } -- Get a reference a[2] = "ff1" -- Change original array and la return la end local fcArray = { "F1", "F2", "F3" } local frArray = ProcessArray(fcArray) -- Putting arrays in an array, and other objects too local subArray1 = { "com", "org", "net", "int" } local subArray2 = { "edu", "mil", "gov" } local multiDimensionalArray = { 22 / 7, -- You can compute values, of course "TLDs", subArray1, subArray2, StupidFunction, 42, } local result = "" result = result .. FormatArrayMessage("simple", simpleArray) result = result .. FormatArrayMessage("predefined", predefinedArray) result = result .. FormatArrayMessage("initialized", initializedArray) result = result .. FormatArrayMessage("generated 1", generatedArray1) result = result .. FormatArrayMessage("generated 2", generatedArray2) result = result .. FormatArrayMessage("original", gArray) result = result .. FormatArrayMessage("assigned", aArray) result = result .. FormatArrayMessage("function call", fcArray) result = result .. FormatArrayMessage("function return", frArray) result = result .. FormatArrayMessage("multi-dimensional", multiDimensionalArray) AddResult(result, "Array Init") -- Getting values: result = "" result = result .. simpleArray[3] .. eol result = result .. predefinedArray[3] .. eol result = result .. initializedArray[1 + 2] .. eol result = result .. multiDimensionalArray[3][2] .. eol result = result .. "[2] is: " .. predefinedArray[2] .. eol result = result .. "[100] is: " .. (predefinedArray[100] or 'nil') .. eol AddResult(result, "Array Access") -- Some array operations local r result = "" --~ predefinedArray[8] = "POP3" -- Using table.insert updates the table size despite the holes, what [] doesn't do -- As such, table.sort successfully takes out the empty slots with below function table.insert(predefinedArray, 8, "POP3") table.insert(predefinedArray, "Z1") table.insert(predefinedArray, "Z2") result = result .. FormatArrayMessage("array", predefinedArray) .. -- getn is deprecated in Lua 5.1 but still works... Should use #predefinedArray in this version. FormatMessage("getn", table.getn(predefinedArray)) .. FormatMessage("type=='table'", tostring(type(predefinedArray) == 'table')) .. eol -- Mutator methods table.sort(predefinedArray, -- I provide a sort function because default doesn't like empty elements... function (a, b) if a == nil then return false elseif b == nil then return true else return a < b end end ) result = result .. FormatArrayMessage("sort", predefinedArray) table.remove(predefinedArray) -- Remove last element result = result .. FormatArrayMessage("remove (pop)", predefinedArray, "") Reverse(predefinedArray) result = result .. FormatArrayMessage("(reverse)", predefinedArray) table.remove(predefinedArray, 1) -- Remove first element result = result .. FormatArrayMessage("remove 1 (shift)", predefinedArray, "") predefinedArray[1] = "HTTPS" result = result .. FormatArrayMessage("(replace) 1", predefinedArray) r = Splice(predefinedArray, 2, 2, "messaging") -- Remove 2, insert 1 in place result = result .. FormatArrayMessage("Splice 2 2 s", predefinedArray, " '" .. (DumpObject(r)) .. "' removed") r = Splice(predefinedArray, 4, 1) -- Remove 1 result = result .. FormatArrayMessage("Splice 4 1", predefinedArray, " '" .. (DumpObject(r)) .. "' removed") r = Splice(predefinedArray, 2, 0, "POP3", "IMAP4") -- Insert 2 result = result .. FormatArrayMessage("Splice 2 0 s s", predefinedArray, " '" .. (DumpObject(r)) .. "' removed") -- Add at end, no simple way to do it one op table.insert(predefinedArray, "Y") table.insert(predefinedArray, "Z") result = result .. FormatArrayMessage("insert Y Z (push)", predefinedArray) -- Add at beginning, idem table.insert(predefinedArray, 1, "B") table.insert(predefinedArray, 1, "A") result = result .. FormatArrayMessage("insert B A (unshift)", predefinedArray) -- Accessor methods r = Slice(predefinedArray, 3, 7) -- Take a slice, from position 3 up to (not including) position 7 result = result .. FormatArrayMessage("slice 3 7", r) r = Slice(predefinedArray, 7) -- Take a slice, from position 7 up to end result = result .. FormatArrayMessage("slice 7", r) predefinedArray = Slice(predefinedArray, 3, -2) -- Remove two elements at both ends result = result .. FormatArrayMessage("slice 3 -2", predefinedArray) predefinedArray = Concat(predefinedArray, simpleArray, initializedArray) result = result .. FormatArrayMessage("(concat/merge) a1 a2 a3", predefinedArray) result = result .. "No native search in values, just iterate on array to search" .. eol result = result .. FormatMessage("concat ' | '", table.concat(predefinedArray, ' | ')) AddResult(result, "Array Operations") function PrintElement(key, value) result = result .. "[" .. key .. "] " .. value .. ", " end result = PadMessage"for ipairs" for i in ipairs(predefinedArray) do PrintElement(i, predefinedArray[i]) end result = result .. eol result = result .. PadMessage"for pairs" for i in pairs(predefinedArray) do PrintElement(i, predefinedArray[i]) end result = result .. eol AddResult(result, "Array Iteration") ---- Associative arrays testPart = testPart + 2 -- Manual creation local tableSimple = {} tableSimple["one"] = "1" tableSimple["two"] = "2_2" tableSimple["three"] = "3_3_3" tableSimple["#@!\\%"] = "garbage" -- Table literal local tableLiteral = { ["one"] = "un_uno_ichi", ["two"] = "deux_dos_ni", ["three"] = "trois_tres_san", ["and... more"] = "...", } -- If the key name is a valid identifier, the quotes can be omitted local tableId = { one = "1", un = 1, uno = true, ichi = "" } -- The values can be any object local tableObj = { ["1, 3.14"] = "1" .. StupidFunction(", " .. 22 / 7), 'in first numerical slot', _ = tableId, [5] = StupidFunction, __ = { 4, 2 }, [3.14159] = aArray, 'in numerical slot after last one', } local tableNested = { deep = { into = { table = '!' } } } local od = "" result = "" result = result .. FormatArrayMessage("simple", tableSimple) result = result .. FormatArrayMessage("literal", tableLiteral) result = result .. FormatArrayMessage("id", tableId) result = result .. FormatArrayMessage("obj", tableObj) result = result .. FormatArrayMessage("nested", tableNested) AddResult(result, "Associative Array Init") -- Getting values: result = "" result = result .. tableSimple["two"] .. eol result = result .. tableSimple.three .. eol result = result .. tableSimple["#@!\\%"] .. eol -- No shortcut here result = result .. (tableSimple[1] or 'nil') .. eol -- Doesn't exist result = result .. (tableSimple.unknown or 'nil') .. eol -- Doesn't exist result = result .. tostring(tableObj._.uno) .. eol result = result .. tableNested.deep.into.table .. eol AddResult(result, "Associative Array Access") -- Some array operations result = "" tableLiteral.newOne = "New entry" tableLiteral[1] = 1 tableLiteral["+-/*"] = "ops" result = result .. FormatArrayMessage("(add)", tableLiteral) tableLiteral.two = nil tableLiteral[1] = nil tableLiteral["and... more"] = nil result = result .. FormatArrayMessage("(delete)", tableLiteral) AddResult(result, "Associative Array Operations") -- Iteration is shown in the DumpObject function... end -- function DoTest() -- Emulation of functions found in other languages -- Split the array str along the pattern delim, up to maxNb (default as no limits) items. -- delim is part of a regular expression, you might need to escape some chars. function Split(str, delim, maxNb) -- Eliminate bad cases... if string.find(str, delim) == nil then return { str } end if maxNb == nil or maxNb < 1 then maxNb = 0 -- No limit end local result = {} local pat = "(.-)" .. delim .. "()" local nb = 0 local lastPos for part, pos in string.gfind(str, pat) do nb = nb + 1 result[nb] = part lastPos = pos if nb == maxNb then break end end -- Handle the last field if nb ~= maxNb then result[nb + 1] = string.sub(str, lastPos) end return result end function Reverse(t) local l = table.getn(t) -- table length local j = l for i = 1, l / 2 do t[i], t[j] = t[j], t[i] j = j - 1 end end -- Emulate the splice function of JS (or array_splice of PHP) -- I keep the imperfect parameter names from the Mozilla doc. -- http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice -- I use 1-based indices, of course. function Splice(t, index, howMany, ...) local removed = {} local tableSize = table.getn(t) -- Table size -- Lua 5.0 handling of vararg... local argNb = table.getn(arg) -- Number of elements to insert -- Check parameter validity if index < 1 then index = 1 end if howMany < 0 then howMany = 0 end if index > tableSize then index = tableSize + 1 -- At end howMany = 0 -- Nothing to delete end if index + howMany - 1 > tableSize then howMany = tableSize - index + 1 -- Adjust to number of elements at index end local argIdx = 1 -- Index in arg -- Replace min(howMany, argNb) entries for pos = index, index + math.min(howMany, argNb) - 1 do -- Copy removed entry table.insert(removed, t[pos]) -- Overwrite entry t[pos] = arg[argIdx] argIdx = argIdx + 1 end argIdx = argIdx - 1 -- If howMany > argNb, remove extra entries for i = 1, howMany - argNb do table.insert(removed, table.remove(t, index + argIdx)) end -- If howMany < argNb, insert remaining new entries for i = argNb - howMany, 1, -1 do table.insert(t, index + howMany, arg[argIdx + i]) end return removed end -- Emulate the slice function of JS (or array_slice of PHP) -- http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice -- I use 1-based indices, of course. function Slice(t, startPos, endPos) local tableSize = table.getn(t) -- Table size if endPos == nil then -- Only one parameter: extract to end of table endPos = tableSize + 1 end if startPos < 0 then startPos = tableSize + 1 + startPos -- -1 -> last element -- -2 -> last two elements, etc. end if endPos < 0 then endPos = tableSize + 1 + endPos end local result = {} for i = startPos, endPos - 1 do result[i - startPos + 1] = t[i] end return result end -- Emulate the concat function of JS (or (more or less) array_merge of PHP) -- http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat -- Different of Lua's table.concat! function Concat(...) local argNb = table.getn(arg) -- Number of elements local result = {} for i = 1, argNb do local a = arg[i] if type(a) == 'table' then for it = 1, table.getn(a) do table.insert(result, a[it]) end else table.insert(result, arg[i]) end end return result end -- Helper functions function DumpObject(obj) local result = "" local len = 0 if type(obj) ~= 'table' then return tostring(obj), 0 end -- First, walk the "array" part, with successive numerical indexes -- As with the PHP and JS scripts, just put the values, indexes are implicit for idx, value in ipairs(obj) do if type(value) == 'table' then break end -- Stop there result = result .. tostring(value) .. "," len = len + 1 end local arraySize = len -- Walk the whole table for key, value in pairs(obj) do if type(key) == 'number' and key == math.floor(key) and key <= arraySize then -- Pure integer in ipairs range, already processed -- A continue keyword would be nice here... else -- Find elegant (legal) key notation if type(key) == 'number' then result = result .. "[" .. key .. "]" elseif string.find(key, "^[%w_]+$") ~= nil then -- Pure word, should exclude keywords... result = result .. key else -- Simplified, I don't use too exotic keys here... result = result .. "['" .. tostring(key) .. "']" end if type(value) == 'string' then value = "'" .. value .. "'" trail = ' ' elseif type(value) == 'table' then value = "\n{ " .. (DumpObject(value)) .. " }" trail = '\n' else value = tostring(value) trail = ' ' end result = result .. " = " .. value .. "," .. trail len = len + 1 end end -- Remove trailing separator return string.gsub(result, ",%s*$", ""), len end function PadMessage(message) return message .. ':' .. string.rep(' ', 25 - string.len(message)) end -- Data formating with message function FormatMessage(message, data, after) return PadMessage(message) .. data .. (after or '') .. eol end -- Array formating with message function FormatArrayMessage(message, array, after) local od, len = DumpObject(array) return PadMessage(message) .. od .. " (" .. len .. ")" .. (after or '') .. eol end function AddResult(result, message) table.insert(htmlPage, testPart, "

" .. message .. "

\n
" .. result .. "
") testPart = testPart + 1 end ----- The script itself... -- Template of HTML page htmlPage = { -- 1 [[ Testing Lua array (table) operations ]], -- 2 [[

Testing Lua array/table operations

Array (table with numerical index)

]], -- 3 [[ ]], -- 4 [[

Associative array

]], -- 5 [[ ]], -- 6 [[
View source of script generating this page / Get source of script ]] } DoTest() outputFileName = "TestLuaArray.html" fh = io.open(outputFileName, "w") fh:write(table.concat(htmlPage)) fh:close()