unknown_document

We couldn't find that page... Were you looking for one of these?


Lua Tables as String Buffers
As with most programming languages, Lua strings are immutable , meaning that they cannot be modified after they are created. Changing a string requires creating a new string that consists of the characters of the previous string, plus whatever changes are made to it. While simple concatenation is fine for small changes, creating a new string for every change becomes very inefficient for larger changes and should be avoided when possible. One way to overcome this inefficiency is the string buffer , which offers a more efficient means of "composing" strings from many smaller strings. String buffers are implemented in Lua using tables , and more specifically they are an application of lists . This means that all of the topics discussed in the lists section apply to string buffers , and this section will focus only on the string buffer application. Let's jump right into an example, starting with the following list of strings . Implementing a string buffer involves creating a list , populating it with each component string , then finally compiling the final string using the table.concat function. Let's demonstrate using the following list : local x = { "this" , "is" , "a" , "string" , } then concatenate the list into the final string : print ( table.concat ( x , " " )) -- this is a string Note that each sub-string is separated by a space, which was passed to the table.concat function. We could have passed another separator, such as a - : print ( table.concat ( x , "-" )) -- this-is-a-string That's about all it takes. Of course, compiling the list will generally be more complicated than this example, and will involve one of more of the methods discussed in the list section, but once the list is populated this is all it takes.
Lua Tables
Lua implements a single data structure , which is implemented as a general-purpose collection that encompasses functionality that is usually implemented by several different data structures in other programming languages. This makes tables powerful, but can also make it a bit challenging to work with, and can lead to surprising results if one is not careful. For example, while tables can act as both lists and mappings , most table functions generally make an assumption about how the table is being used. When a table function is used in the wrong context it won't generate an error, it will just return an unexpected result. We find it helpful to think of the table as a Swiss Army Knife ; it can support many functions, but in order to complete a task you have to first determine the role you want the knife to perform, then use the knife in a way that is consistent with that role. For example, if the goal is to open a bottle, then the knife plays the role of a bottle opener. The bottle-opener tool must be selected, and the knife must be used like a bottle-opener. It would be difficult to open a bottle with the toothpick or magnifying glass tools, even if they are used like a bottle-opener. Likewise, it would be equally difficult to open a bottle with the bottle-opener tool if it were to be used like a toothpick or a magnifying glass. With this in mind, rather than discuss tables as a single, all-encompassing data structure, we will identify the role that we want the table to perform, then discuss how to use the tables in that role. Of the many different roles a table can perform, we currently focus on the most common: Lists (also called Arrays) Maps (also called Dictionaries or Associative Arrays) String Buffers
Lua
Lua is a lightweight, cross-platform, high-level programming language that has become increasingly-popular as an embedded scripting language. Lua is often praised as a clean, simple, easy to learn scripting language that is applicable to a wide range of tasks. Today Lua can be found in a wide range of applications ranging from wezterm to xplr and neovim . Let's get started.
Multi-dimensional Lists in Lua
Since lists can contain any type as elements, they can also contain other lists . This allows multi-dimensional lists , sometimes called "matrices", to be created. A multi-dimensional list can be created directly as before: -- initialize a table with values local x = { { "a" , "b" , "c" }, { "d" , "e" , "f" }, { "g" , "h" , "i" }, } Now, when we access the direct descendants of this list we get the nested list . We can access the nested elements themselves by indexing the nested lists : -- access values stored in the table print ( x [ 1 ]) -- table: 0x5e35821553e0 print ( x [ 1 ][ 1 ]) -- a print ( x [ 1 ][ 2 ]) -- b print ( x [ 1 ][ 3 ]) -- c We can also create multi-dimensional lists programmatically as well. In this example we create the list and add elements in a loop : -- initialize an empty list local x = {} for i = 1 , 3 do -- instantiate an empty list to contain the nested content local inner = {} for j = 1 , 3 do inner [ j ] = string.char ( 97 + 3 * ( i - 1 ) + ( j - 1 )) end x [ i ] = inner end This makes use of the string.char function to generate characters from their decimal ASCII codes. After the list is created, its elements can be accessed as usual: -- access values stored in the table print ( x [ 1 ][ 1 ]) -- a print ( x [ 1 ][ 2 ]) -- b print ( x [ 1 ][ 3 ]) -- c print ( x [ 2 ][ 1 ]) -- d print ( x [ 2 ][ 2 ]) -- e print ( x [ 2 ][ 3 ]) -- f print ( x [ 3 ][ 1 ]) -- g print ( x [ 3 ][ 2 ]) -- h print ( x [ 3 ][ 3 ]) -- i
Length of Lua Maps
We discussed Lua's length operator in the context of lists and strings . Now let's take a look at finding the length of a map . Let's first take a look at how the length operator behaves with a map : local x = { a = 1 , b = 2 , c = 3 , d = 4 , e = 5 , } print ( # x ) -- 0 Hmm, that was not expected. The table clearly has 5 values in it, but the result is 0. It turns out the that length operator only considers the number of list-like values contained in a table, and does not consider mappings at all. Getting the number of items in a map requires counting items manually: local count = 0 for key , val in pairs ( x ) do count = count + 1 end print ( count ) -- 5
Lua List Length
Lua includes a length operator # which (in theory) returns the number of items contained in the value to which it is applied. However, it is frankly a bit wonky and can give unexpected results if used outside of a fairly narrow range of applications. Let's take a look: We learned earlier than tables are collections of values. It can often be helpful to know how many values are contained in a collection. Let's look a list-like tables first: Lists Let's create a list then see how many items are in it: local x = { 1 , 2 , 3 , 5 , 6 } print ( # x ) -- 5 Ok, that worked as we might expect. Five values we added to the list, and the length operator returned a length of 5. Let's see what happens with a list of nil values: local y = { nil , nil } print ( # y ) -- 0 Hmm, the list contains 2 nil values, but returns a length of 0. Should we have expected a length of 0 or a length of 2? On one hand there are 2 nil values in the list, but on the other hand nil is considered to be "not a value". Let's leave that debate to the philosophers, the important thing for programmers is that Lua has an opinion about this, and applies that opinion consistently. So, based on this result we can conclude that Lua does not count nil values in lists. Let's look at another example: local x = { 1 , 2 , 3 , nil , 4 , 5 } print ( # x ) -- 6 Er, hold on. Lua counted nil as a value this time? It turns out that the process Lua uses to calculate the length of list-like tables is not as simple as counting values in the list, and gets complicated when the list contains nil values. Even worse, the result that is returned depends not only on the values that are in the list at the time that the length operator is applied, it depends on how the table was created and populated. Let's see what happens when we build this table another way: local y = {} y [ 1 ] = 1 y [ 2 ] = 2 y [ 3 ] = 3 y [ 4 ] = nil y [ 5 ] = 4 y [ 6 ] = 5 print ( # y ) -- 3 Although these tables contain the same values, this table has a length of 3? After digging through the Lua documentation to gain a better understanding of how this operator works, we have learned that the situation is even worse than expected - this example could have returned any of 2, 3, 5, or 6. So, while the length operator can be useful with list-like tables, care must be taken to ensure that the lists it operates don't contain any nil values.
Sorting Lists in Lua
One very common operation on lists is sorting them. Lua's table library contains the table.sort function local x = { "b" , "e" , "a" , "d" , "c" , } Default Sort -- list is sorted in-place table.sort ( x ) print ( x [ 1 ]) -- a print ( x [ 2 ]) -- b print ( x [ 3 ]) -- c print ( x [ 4 ]) -- d print ( x [ 5 ]) -- e Sorting in Reverse -- list is sorted in-place table.sort ( x , function ( a , b ) return a > b end ) print ( x [ 1 ]) -- e print ( x [ 2 ]) -- d print ( x [ 3 ]) -- c print ( x [ 4 ]) -- b print ( x [ 5 ]) -- a Random Order local math = require ( "math" ) -- list is sorted in-place table.sort ( x , function ( a , b ) return math.random () > 0.5 end ) print ( x [ 1 ]) -- d print ( x [ 2 ]) -- a print ( x [ 3 ]) -- b print ( x [ 4 ]) -- c print ( x [ 5 ]) -- e
Creating Lua Maps
Maps , often also called Dictionaries and associative arrays , are data structures that organize elements such that each element can be easily retrieved by its name, or key. Unlike lists maps do not guarantee fixed ordering, values can be assigned to maps in any order, and retrieved in any order. A map is created by instantiating a table of values, as below: -- initialize a mapping of key, value pairs local x = { a = 1 , b = 2 , c = 3 , d = 4 , } Let's try printing the map : print ( x ) -- table: 0x59b68bdd9de0 As we saw in lists , when Lua prints a table it displays the type (table) followed by its address in memory, which can be useful in some situations but generally isn't what we want. Inspecting the values of a map requires accessing them individually. -- access values stored in the table print ( x [ "a" ]) -- 1 print ( x [ "b" ]) -- 2 print ( x [ "c" ]) -- 3 print ( x [ "d" ]) -- 4 -- accessing values *not* in the table returns nil print ( x [ "x" ]) -- nil Lua also allows map values to be accessed by referencing map keys as properties: -- access values stored in the table print ( x . a ) -- 1 print ( x . b ) -- 2 print ( x . c ) -- 3 print ( x . d ) -- 4 -- accessing values *not* in the table returns nil print ( x . x ) -- nil There is a second method for creating maps , which is often helpful when creating maps programmatically such as in a loop . In this this method, an empty map is created and assigned to a variable , then values are assigned directly to each key: -- initialize an empty table local x = {} -- assign values to each key x [ "a" ] = 1 x [ "b" ] = 2 x [ "c" ] = 3 x [ "d" ] = 4 After the map is created, its elements can be accessed as usual: -- access values stored in the table print ( x [ "a" ]) -- 1 print ( x [ "b" ]) -- 2 print ( x . c ) -- 3 print ( x . d ) -- 4
Creating Lists in Lua
Lists , often also called arrays , are data structures that organize elements such as values by index. A list is created by instantiating a table of values, as below: -- initialize a table with values local x = { "a" , "b" , "c" } Let's try printing the list : print ( x ) -- table: 0x6121ee19eda0 By default, when Lua prints a table it displays the type (table) followed by its address in memory, which can be useful in some situations but generally isn't what we want. Inspecting the values of a list requires accessing them individually. -- access values stored in the table print ( x [ 1 ]) -- a print ( x [ 2 ]) -- b print ( x [ 3 ]) -- c -- accessing values *not* in the table returns nil print ( x [ 9 ]) -- nil There is a second method for creating lists , which is often helpful when creating lists programmatically such as in a loop . In this this method, an empty list is created and assigned to a variable , then values are assigned directly by index: -- initialize an empty table local x = {} -- assign values by index x [ 1 ] = "d" x [ 2 ] = "e" x [ 3 ] = "f" After the list is created, its elements can be accessed as usual: -- access values stored in the table print ( x [ 1 ]) -- d print ( x [ 2 ]) -- e print ( x [ 3 ]) -- f A variation on this method uses the table.insert method to append values to the list , and will be discussed shortly .
Modifying Lua Maps
Maps can be modified after they are created. Starting from the following map : local x = { a = 1 , b = 2 , c = 3 , d = 4 , } let's look at modifying existing values in the map . As a saw in the previous section we can access elements in the map by retrieving the key from the map , and also by accessing each key as a property of the map . We can also use either method to assign values to the map : -- update a value in the table by key x [ "b" ] = 9 -- update value by property x . c = 11 print ( x [ "a" ]) -- 1 print ( x [ "b" ]) -- 9 print ( x [ "c" ]) -- 11 print ( x [ "d" ]) -- 4 Inserting a value is works the same way: we simply create a new key by assigning a value to it: -- insert a value in the table x [ "e" ] = 5 x . f = 6 print ( x [ "a" ]) -- 1 print ( x [ "b" ]) -- 9 print ( x [ "c" ]) -- 11 print ( x [ "d" ]) -- 4 print ( x [ "e" ]) -- 5 print ( x [ "f" ]) -- 6 Removing a value from the map is done by assigning nil to it: -- remove an item from the map x . e = nil print ( x [ "a" ]) -- 1 print ( x [ "b" ]) -- 9 print ( x [ "c" ]) -- 11 print ( x [ "d" ]) -- 4 print ( x [ "e" ]) -- nil print ( x [ "f" ]) -- 6