Lua full tutorial

This article directory

  • Lua profile
  • Lua version
  • Lua environment
    • The development tools
    • Software Package Management
    • Analysis and debugging
  • Basic concept
    • Constants and identifiers
    • Variables and data types
    • Expression
    • Statement
  • idioms
  • Advanced features
    • High level syntax
      • Metatable
      • Environment
      • Closure
      • Coroutine
    • Code organization
      • Module
        • Definition module
        • Use the module
        • Module to find
      • Package (package)
    • object-oriented
    • Language interoperation
      • C API
      • FFI
  • other
    • Command line arguments
    • A decorator
  • Optimization Suggestions
  • Commonly used class library
  • The resources
  • Footnotes

Lua profile

#! /usr/bin/env lua print("Hello World!" )Copy the code

Lua (LOO- AH) is an embeddable, lightweight, fast, powerful scripting language. It supports procedural programming, object-oriented programming, functional programming, data-driven programming and data description.

Lua combines a concise procedural syntax with a data-description syntax structure based on associative arrays and extensible semantics. Lua is a dynamically typed language that uses registrie-based virtual machines to interpret and run bytecode, and uses incremental garbage collection to manage memory automatically. These characteristics make Lua ideal for configuration, scripting, and rapid prototyping scenarios.

Lua was the first highly popular scripting language (and the leading scripting language in Games) developed by Third World (Brazil) developers.

The Lua interpreter is only 2W + multi-line ANSI C/C++ code with an executable file size of 200+ KB.

Here are a few well-known applications that embed the Lua interpreter and can use the Lua extensions:

  • World of Warcraft
  • Angry Birds
  • Redis
  • Wireshark
  • Wrk
  • Nmap
  • MySQL Workbench
  • VLC
  • Other industrial applications

Lua version

Lua officially released version 5.2 in 2011 and version 5.3 in 2015. Compared to the massively popular version 5.1 released in 2006, Lua syntax and C API are incompatible. OpenResty and LuaJIT support for these two latest releases is difficult:

https://openresty.org/en/faq.html Lua 5.2 + are incompatible with the Lua 5.1 on both the C API land and the Lua land (including various language semantics)... Lua 5.2+ are essentially incompatible different languages. Supporting Lua 5.2+ requires nontrivial architectural changes In ngx_Lua's basic infrastructure. The most Worrying thing is The quite different "environments" model in Lua 5.2+. At this point, We would hold back ading suport for Lua 5.2+ to ngx_Lua. https://www.reddit.com/r/lua/comments/2zutj8/mike_pall_luajit_dislikes_lua_53 Mike Pall (LuaJIT) dislikes Lua 5.3Copy the code

All in all, Lua, like Python, has shaken off the baggage of “forward compatibility” and moved forward in leaps and bounds. This is good for the language itself, but painful for its users in the short term.

Since we are studying Lua this time to prepare for OpenResty development, the concepts and examples in this article will focus on Lua 5.1.

Lua environment

The development tools

  • Vim + Syntastic
  • ZeroBrane Studio
  • Decoda (Windows only)
  • Lua for IntelliJ IDEA
  • Babelua for Visual Studio
  • lua-checker/LuaInspect
  • LuaDoc
  • And more <http://lua-users.org/wiki/LuaIntegratedDevelopmentEnvironments>

Software Package Management

  • LuaRocks

Analysis and debugging

  • print(), and tracing:

    [Wikipedia: https://en.wikipedia.org/wiki/Tracing_(software)] Tracing involves a specialized use of logging to record information to  record information about a program's execution. This information is typically used by programmers for debugging purposes. Logging - error reporting. Tracing - following your program flow and data to find out where the performance bottlenecks are and even more important when an error occurs you have a chance to find out how you did get there. In an ideal world every function would have some tracing enabled with the function duration, passed parameters and how far you did get in your function.Copy the code
  • luatrace – Toolset for tracing/analyzing/profiling script execution and generating detailed reports.

  • luaprofiler – Time profiler for the Lua programming language.

  • StackTracePlus – Drop-in upgrade to Lua’s stack traces which adds local context and improves readability.

  • MobDebug – Powerful remote debugger with breakpoints and stack inspection. Used by ZeroBraneStudio.

  • Debug interface debug provided by Lua itself.

For more tools: lua-users.org/wiki/Progra…

Basic concept

Constants and identifiers

  • Language keywords

    and, break, do, else, elseif, end, false, for, function, if, in,
    local, nil, not, or, repeat, return, then, true, until, while
    Copy the code
  • Other identifiers

    + - * / % ^ # == ~= <= >= < >= () {} []; :,... . -- ',' is not an operator in Lua, but only a delimiterCopy the code
  • Constant strings are separated by ‘or “, where c-like sequences of escaped characters can be used:

    \a, \b, \f, \n, \r, \t, \v, \\, \", \ddd, \0
    Copy the code
  • As a convention, Lua keeps all uppercase variable names starting with _ as internal global variables;

    _VERSION
    Copy the code
  • Constant strings can be expressed using the Long Brackets syntax:

    • [[opening bracket]]closing bracket. You can use=Representation hierarchy, for example,[[Represents level 0,[= = = = [Represents level 4;
    • If there is an escape sequence, the escape sequence will not be escaped by Lua.
    • opening bracketsA line break immediately following it is ignored by Lua;
    • opening bracketsIt’s going to be on the same level as the first timeclosing bracketsMatching;
    a = [=[
    abc
    xyz]=]
    Copy the code
  • Lua uses double precision floating point numbers to represent numeric constants. Data constants can be in hexadecimal or scientific notation:

    3, 3.0, 3.1416, 314.16e-2, 0xff, 0x56
    Copy the code
  • There are two types of Lua comment: short comment and long comment:

    -- short comment
    
    --[[
         this is a very loooooooooooooong
         comment
    ]]
    
    --[=[
        Same scheme used with long string.
    ]=]
    Copy the code

Variables and data types

  • Lua is a dynamically typed language, which means:

    • Variables have no type information, but values have type information.
    • There is no type definition statement in the code, the type information is carried by “value”;
    • All “values” are first-class and can be stored in variables, passed as arguments to other functions, or returned by functions;
  • Lua provides eight basic types:

    • Nil – The only value defined under this type is nil. Its main property is that it does not look like any other value. In general, nil is used in situations where other meaningful values are missing.

    • Boolean – Values defined under this type can be false and true. In Lua conditional expressions, all types are “true” except nil and false (for example, 0 and “are” true “in Lua).

    • Number-lua uses the double precision floating point type to store values of this type by default.

    • String – A value of this type can be composed of any 8-bit character. Lua keeps a copy of the same character string in memory (interning), and does not allow modification of string constants (immutable).

    • Function – Values of this type are functions written in Lua or C.

    • Userdata – Values of this type are C data that, from Lua’s point of view, correspond to a chunk of bare memory with no predefined behavior. Lua does not allow you to create or modify userData using Lua code, but allows you to do so using the C API. In addition, the user can set the Metatable to userData, defining more operations that can be performed on it.

    • Thread – The value of this type is the Lua thread through which Lua implements coroutine functionality.

    • Table – The value of this type is an associative array. This type consists of an array part and a lexical part. The array part holds contiguous data indexed to integers starting at 1; The dictionary section holds the rest of the data (including data whose index is an integer, but not [1, #table]).

      • Table is the only data structure construction mechanism provided by Lua: it can be used to represent array, Symbol table, set, record, graph, tree and other data structures;

      • Nil does not apply to index of table (error: table index is nil);

      • Nil is not a valid table element value. If you assign nil to an element, that element is deleted from the table.

      • Lua provides the following methods for creating tables:

        -- A field of the form `name = exp` is equivalent to -- `["name"] = exp`; A field of form `exp` is equivalent to -- `[i] = exp`, where `i` are consecutive numerical integers, -- starting with 1. a = { ["name"]= "dhb"; "male", [10]= 25, addr= "beijing", job= "code monkey"; } -- or a = {} a["name"] = "dhb" a[1] = "male" a[10] = 25 a["addr"] = "beijing" a.job = "code monkey" a.removed = nil --  this field will be removed by gcCopy the code
      • Lua provides syntactic sugar for the use of a[“name”] : a.name;

    • Table, function, thread, and userData values are referenced rather than copied in assignment, parameter passing, and function return values.

    • You can use the type() function to get a string describing the value type;

    • At runtime, Lua automatically converts string and number values to and from each other based on the context (as a strongly typed language, Lua only supports the following implicit conversions) :

      • In arithmetic operations, string is converted to number;

        10 + 10 ""Copy the code
      • Convert number to string when used in situations where string (where a string is expected) is expected.

        -- valid
        10 .. " boxes"
        
        -- invalid
        10 == "10"
        Copy the code
  • Lua has three types of variables: global variables, local variables, and table fields:

    • Variables default to global variables [3];

    • Function arguments are local variables;

    • Variables defined using local are local variables, and Lua uses lexically scoped, or statically scoped for them [1].

      function foo()
          function b() print(type(a), a) end
          local a = 10
          function f() print(type(a), a) end
          b(); f()
      end
      
      foo()
      Copy the code
      • Local variables have their scope limited to the block where they are declared: The scope of a local variable begins at the first statement after its declaration and lasts until the last non-void statement of the innermost block that includes the declaration.

        -- example.lua
        local a = 10
        b = 10
        
        print(_G.g, _G.h)     -- nil, 10
        Copy the code
      • A block is a list of statements; syntactically, a block is the same as a chunk:

        A chunk is an outermost block which you feed to "load()"
        -- Roberto
        Copy the code
      • Lua handles a chunk as the body of an anonymous function with a variable number of arguments. As such, chunks can define local variables, receive arguments, and return values.

      • Explicit blocks are useful to control the scope of variable declarations. Explicit blocks are sometimes used to add a return or break statement in the middle of another block.

      -- explicit block
      do
          local a = 10
          print(type(a), a)
      end
      print(type(a), a)
      
      -- control structure
      if true then
          local a = 10
          print(type(a), a)
      end
      print(type(a), a)
      Copy the code
      • Internal functions defined in the scope of a local variable can use this local variable. For internal functions, this local variable is called upvalue, or external local variable.

        do
            local a = 10
        
            function bar()
                print(a)      -- `a` is called upvalue for `bar`
            end
        end
        Copy the code
      • Each time the local statement is used, a new local variable is created;

        a = {}                -- global variable
        local x = 20
        for i = 1, 10 do
            local y = 0
            a[i] = function () y = y + 1; return x + y end
        end
        Copy the code
    • Unassigned variables default to nil, so it is best to define variables before they are used (the Idiom section below provides several ways to detect undefined variables in code);

      -- error in Lua
      function foo()
          local function bar() zog() end
          local function zog() print "Zog!" end
          bar()
      end
      Copy the code
      # valid in Python
      def foo():
          def bar():
              zog()
          def zog():
              print "Zog!"
          bar()
      Copy the code
    • Lua uses a table to hold all global variables. This table is called environment Table, or environment.

      • Every function has a pairenvironment tableIn this way, the lookup of all global variables in a function is done through it;
      • When a function is created, it inherits from the creatorenvironment
      • In the function, you can passgetfenv()Explicitly gets what is used on itenvironmentThe lead; Also throughsetfenv()Using the newenvironmentReplace the originalenvironment

Expression

  • Operator: arithmetic operator, relational operators, logical operators, concatenation operator, unary minus, unary not, unary length operator.

    • The logical NOT operator yields either true or false; The logical AND and or operators are short-circuited, resulting in either the first or the second operand.

      a = 10 and nil or 11
      Copy the code
    • The length operator # can be used to get the number of bytes in a constant string and the number of elements in a table array:

      #"abcd" -- 4 #{1, 2, 3} -- 3 #{[0]= 0, [1]= 1, [2]= 2} -- ? #{[2]= 2, [3]= 3, [4]= 4} --? #{[1]= 1, name= "dhb"} -- ?Copy the code
    • In addition to.. And ^ are right associative, and all other operators are left associative.

      From Wikipedia:
      
      The associativity of an operator is a property that determines how
      operators of the same precedence are grouped in the absence of
      parentheses. If an operand is both preceded and followed by operators,
      and those operators have equal precedence, then the operand may be
      used as input to two different operations. The choice of which
      operations to apply the operand to, is determined by the
      "associativity" of the operators.
      
      * Left-associative - the operations are grouped from the left.
      * Right-associative - the operations are grouped from the right.
      Copy the code
    • Operator priority see [2]

      6 + 2-7 ^ 2 ^ 2 --?Copy the code
    • Lua provides a special string concatenation operator.. ;

      Overloading `+` to mean string concatenation is a long tradition. But
      concatenation is not addition, and it is useful to keep the concepts
      separate, In Lua, strings can convert into numbers when appropriate
      (e.g 10 + "20") and numbers can convert into strings
      (e.g 10 ..  "hello"). Having separate operators means that there is no
      confusion, as famously happens in JavaScript.
      Copy the code
  • Expressions: constant expressions, arithmetic expressions, relational expressions, logical expressions, join expressions, Vararg expressions, function call expressions, function declarations, table construction expressions, and so on;

    • A few examples of relational expressions:

      local t1, t2 = {}, {}
      print(t1 == t2)         -- false. Tables are never compared "element by
                              -- element".
      
      local s1, s2 = "abc", "abc"
      print(s1 == s2)         -- true. There is only ever one instance of any
                              -- particular string stored in memory, so
                              -- comparison is very quick (interning). And
                              -- strings are "immutable", there is no way in
                              -- Lua to modify the contents of a string
                              -- directly
      Copy the code
    • A function definition is an executable expression that returns a value of type function:

      function f() body end               -- equivalent to
      f = function () body end
      
      function a.b.c.f() body end         -- equivalent to
      a.b.c.f = function () body end
      
      local function f() body end         -- equivalent to
      local f; f = function () body end
      Copy the code
      • Function definitions can support mutable arguments through varargs expressions. The varargs expression appears at the end of the function argument list, and it receives the extra arguments for use in the function body. Since Varargs also returns multiple values, Lua treats its return value like a function call.

        function foo(...) -- The expression `... ` behaves like a multiple return function -- returning all varargs of the current function. local a, b, c = ... -... -- The expression `{... }` results in a array with all collected -- arguments. for i, v in ipairs{... } do -- ... end -- ... -- Whe the varargs list may contain valid `nil`s, we can use the -- `select` function to get specific arguments. local args = {n= select("#", ...) . } for i = 1, args.n do print(args[i]) end endCopy the code
    • Function call expression syntax is similar to other languages in that expressions in parameters are evaluated before the function call occurs. Meanwhile, Lua provides two syntactic sugars on top of this:

      • v:name(args)v.name(v, args)Grammatical sugar, but also, implicitly,vObject will be passed tonameThe first argument to the functionself
      • If there is only one parameter and its type isstring, can be usedf"string"Call a function in the form of; If there is only one parameter and its type istable, can be usedf{fields}Call a function in the form of;

Statement

  • Assignment statement

    i = 3
    i, a[i] = i + 1, 20
    
    x, y, z = y, z, x
    
    -- and cannot do following because it's a statement
    a = b = 1
    if (a = 1) then ... end
    Copy the code
    • Lua evaluates the expressions contained on both sides of an assignment statement before performing the assignment.

    • Before the assignment, Lua adjusts the list of values on the right based on the variable data on the left. If the number of values on the right is greater than the number of variables on the left, the excess is discarded. If the number of values on the right is less than the number of variables on the left, Lua uses nil to replenish the list of values; If the last function on the right is a function call, all the return values of the function are added to the list of values;

      -- in Lua
      x, y = {1, 2}       -- maybe `unpack` is needed
      Copy the code
      # in Python
      x, y = (1, 2)
      Copy the code
    • The parameter value transfer rule of function call is consistent with the assignment statement; Lua also has the following rules for handling function return values:

      • Lua discards all return values by default if a function call is used by a separate statement.

        foo()
        Copy the code
      • (and) enclosed expressions, including function calls, Lua keeps the first return value as the value of the entire expression;

        function foo() return 1, 2, 3 end
        x, y, z = (foo())
        Copy the code
      • When a function call is used as part of an expression, Lua keeps its first return value;

        function foo() return 1, 2, 3 end
        print(foo())
        print(1 + foo())
        Copy the code
      • In addition to the above description, Lua adds all the return values of a function call that appears at the end of an assignment statement’s list of values or at the end of a function call’s argument list to the list of values. Lua uses all of its return values at the end of a table constructor and return statement’s list of return values.

        function foo() return 1, 2, 3 end
        t1 = {foo()}
        t2 = {4, foo()}
        t3 = {foo(), 5}
        Copy the code
      • Difference from Python

        -- in Lua
        x, y = foo()
        Copy the code
        # in Python
        x, y = foo()      # a tuple and implicit unpack involved
        Copy the code
  • Control structure

    while exp do block end repeat block until exp if exp then block {elseif exp then block} [else block] end -- numeric ``for`` for var = exp, exp [, exp] do block end -- generic ``for`` for var [, var...]  in explist do block end -- ``explist`` is evaluated only once. Its results are an *iterator* -- function, a *state*, and an initial value for the first *iterator* -- variable break return -- but no ``continue``Copy the code
  • Other statements

    -- Function calls as statements
    foo()
    -- Local declaration
    local var
    Copy the code

idioms

The Lua way.
Copy the code
  • Memory without any references is automatically released by the garbage collection mechanism. In general, the release time is selected by the Lua interpreter. Developers can force the Lua resolver to do garbage collection by calling CollectGarbage (“collect”), but it usually requires two consecutive calls;

  • If you want to record the number of elements in the sparse array, the user needs to save and maintain the number of elements through the counter.

    local t = {counter= 0}
    t[2] = "dhb"
    t.counter = t.counter + 1
    Copy the code
  • Error messages from Lua functions are usually returned to the caller with a return value. Usually, if the first return value of a function is nil or false, the second return value is the actual error message.

  • Using pcall/ xpCall in Lua allows you to do the same thing with try/catch in other languages. Error is a throw or raise used to throw an exception in other languages.

    local ok, err = pcall(function()
        t.alpha = 2.0    -- will throw an error if `t` is nil or not a table
    end)
    if not ok then
        print(err)
    end
    Copy the code
  • Callback function:

    Callback function is one of the most powerful programming paradigms because
    it enables a general purpose function to do very specific things.
    Copy the code
  • Lua function definitions and function calls do not support “named arguments”. If you need a similar argument, you can use a table to save the argument and then use that table as the only argument to the function.

    function foo(args) local name = args.name or "anonymous" local os = args.os or "Linux" local email = args.os or name .. "@".. os ... end foo{name= "bill", os="windows"}Copy the code
  • All global variables are stored in a table named _G;

    a = 10
    print(a, _G.a)
    _G._G == _G
    Copy the code
  • There is no difference between a single line ‘or a double quote “when representing a constant string. Escape sequences can be used to represent special characters in a string.

  • Print the table

    --[[
    This is not very efficient for big tables because of all the string
    concatenations involved, and will freak if you have *circular references
    or 'cycles'
    ]]
    function dumptable(o)
        if type(o) == 'table' then
            local s = "{"
            for k, v in pairs(o) do
                if type(k) ~= 'number' then k = '"' .. k .. '"' end
                s = s .. '[' .. k .. '] = ' .. dumptable(v) .. ','
            end
            return s .. '}'
        else
            return tostring(o)
        end
    end
    
    -- More options: http://lua-users.org/wiki/TableSerialization
    Copy the code
  • Read the file

    • Read the file using the io.lines function. This function opens and closes files automatically:

      for line in io.lines "myfile" do
          ...
      end
      Copy the code
    • Open and IO. Close explicitly open and close files:

      local f, err = io.open("myfile")
      if not f then return print(err) end
      for line in f:lines() do
          ...
      end
      f:close()
      
      -- alternative reading
      local line = f:read '*l'
      while line do
          ...
          line = f:read '*l'
      end
      
      -- to read the whole file
      local s = f:read '*a'
      Copy the code
  • Some examples of string manipulation [5]

  • At run time, Lua defaults to looking for unknown variables from the global or module environment, and if the variable is undefined, Lua returns nil as its value. Nil is also a legal variable value in Lua, so at run time, it’s hard to tell if a variable’s value is nil or if it’s not defined. This mechanism is not always good news for variable names spelled incorrectly. In Lua, the following idiom is used to detect undeclared variables in code:

    <http://lua-users.org/wiki/DetectingUndefinedVariables> In Lua programs, typos in variable names can be hard to spot because, in general, Lua will not complain that a variable is undefined... If a variable if not recognized by Lua as a local variable (e.g. by static declaration of the variable using a "local" keyword or function parameter definition), the variable is instead intepreted as a global variable... Whether a global varaible is defined is not as easy to determine or describe.Copy the code
    • Runtime detection

      • By overloading the __index and __newindex fields of the metatable of the current function environment (the following implementation is directly for the global environment), read and write operations on global undefined variables can be detected at run time and runtime errors can be thrown. The downside of this approach is that it can only be used at runtime; Undefined variable references in code not executed (not overwritten) at runtime cannot be detected;

        • strict module in the Lua distribution (etc/strict.lua);
        • LuaStrict by ThomasLauer for an extension of the strict approach;
      • Niklas Frykholm implements a module for enforcing local variable definitions. It requires that all variables must be defined as local variables using local variables defined as undefined variables. This implementation is equivalent to an extended version of the above approach, which is more elegant and less intrusive;

        --===================================================
        --=  Niklas Frykholm
        -- basically if user tries to create global variable
        -- the system will not let them!!
        -- call GLOBAL_lock(_G)
        --
        --===================================================
        function GLOBAL_lock(t)
          local mt = getmetatable(t) or {}
          mt.__newindex = lock_new_index
          setmetatable(t, mt)
        end
        
        --===================================================
        -- call GLOBAL_unlock(_G)
        -- to change things back to normal.
        --===================================================
        function GLOBAL_unlock(t)
          local mt = getmetatable(t) or {}
          mt.__newindex = unlock_new_index
          setmetatable(t, mt)
        end
        
        function lock_new_index(t, k, v)
          if (k~="_" and string.sub(k,1,2) ~= "__") then
            GLOBAL_unlock(_G)
            error("GLOBALS are locked -- " .. k ..
                  " must be declared local or prefix with '__' for globals.", 2)
          else
            rawset(t, k, v)
          end
        end
        
        function unlock_new_index(t, k, v)
          rawset(t, k, v)
        end
        
        -- Basically anytime you call ``GLOBAL_lock(_G)`` somewhere in your
        -- code, from that point onwards anytime you try to use a variable
        -- without explicitly declaring it as 'local', Lua will raise an error.
        Copy the code
    • Static analysis detection – In addition to runtime dynamic detection, we can also use the static analysis method to detect undefined variables before the code runs:

      • Using luac

        # This lists all gets and sets to global variables (both defined and
        # undefined ones).
        % luac -p -l myprogram.lua | grep ETGLOBAL
        Copy the code
      • Other tools

        • LuaLint
        • lglob
        • globals-lua
        • LuaInspect
        • LuaChecker
        • IDEs, etc.
    • A hybrid runtime/static analysis approach

  • Ipairs can be used to traverse the array portion of a table in order of index from smallest to largest; Pairs can iterate over all elements in a table, but the output is unordered;

  • If you need to call a string handler for a constant character, you can use (“string”):method(…) In the form of:

    ("%s=%d"):format("hello", 42)  -- is equivalent to
    string.format("%s=%d", "hello", 42)
    Copy the code

Advanced features

High level syntax

Metatable
Every value in Lua can have a *metatable*.
Copy the code

Lua uses metatable to define the behavior of data (original value) under special operations (arithmetic operation, small and large comparison, join operation, length operation, index operation, etc.).

We refer to the specific operations that Metatable supports as events, and the corresponding actions are represented by Metamethod. The Metatable is actually a normal table. The event name is prefixed with an underscore of __ as the key index of metatable, and the value of the index is metamethod. For example, when a value of a non-numeric type is used as an arithmetic plus operand, Lua uses the metamethod corresponding to __add in Metatable to do the arithmetic plus.

The main events provided by Metatable are:

  • add – the + operation;

  • sub – the - operation;

  • mul – the * operation;

  • div – the / operation;

  • mod – the % operation;

  • pow – the ^ operation;

  • unm – the unary - operation;

  • concat – the .. operation;

  • len – the # operation;

  • eq – the == operation;

    • You can override itmetatable__eqMethod redefines the “equality” rule for objects, but it is important to note that__eqRequires that the two operands participating in the comparison have the same type and use the same__eq metamethod
  • lt – the < operation;

  • le – the <= operation;

  • index – The indexing access table[key];

    ``__index`` fires when Lua cannot find a key inside a table. ``__index`` can be set to either a table or to a function; objects are often implemented by setting ``__index`` to be the metatable itself, and by putting the methods in the metatable. A naive ``Set`` class would put some methods in the metatable and store the  elements of the set as keys in the object itself.Copy the code
    • __indexmetamethodCan betableOr function
    -- simulation
    function gettable_event(table, key)
        local h
        if type(table) == "table" then
            local v = rawget(table, key)
            if v ~= nil then return v end
            h = metatable(table).__index
            if h == nil then return nil end
        else
            h = metatable(table).__index
            if h == nil then error(...) end
        end
        if type(h) == "function" then
            return (h(table, key))  -- call the handler
        else
            return h[key]           -- or repeat operation on it
        end
    end
    Copy the code
  • newindex – the indexing assignment table[key] = value;

  • call – called when Lua calls a value;

    function function_event(func, ...)
        if type(func) == "function" then
            return func(...) -- primitive call
        else
            local h = metatable(func).__call
            if h then
                return h(func, ...)
            else
                error(...)
            end
        end
    end
    Copy the code

In Lua code, you can set a different Metatable for each table and UserData, while the values of each of the other six data types use the same Metatable. In Lua code, only the metatable of table type values can be set and modified. Other metatable types can be modified using the C API.

Userdata is created by THE C API Lua_newUserData, which is different from the memory block created by malloc. The memory occupied by userData is reclaimed by the garbage collector. Metatable can be set up for UserData to customize its behavior. Userdata supports only two types of events: __len and __gc, where the metamethod of __GC is called by the garbage collector. For example, an object of type File provided by the standard library, its __GC metamethod is responsible for closing the underlying document handle.

Alternatively, metatable can define atable as weak table using __mode event:

To understand weak tables, you need to understand a common problem with garbage collection. The collector must be conservative as possible, so cannot make any assumptions about data; it is not allowed to be pyshic. As soon as objects are kept in a table, they are considered referenced and will not be collected as long as that table is referenced. Objects referenced by a weak table will be collected if there is no other reference to them. Putting a value in a table with weak values is effect telling the garbage collector that this is not an important reference and can be safed collected. A weak table can have weak keys, weak values, or both. A table with weak keys allows the collection of its keys, but prevents the collection of its value. A table with both weak keys and weak values allows the collection of both keys  and values. In any case, if either key or the value is collected, the whole pair is removed from the table.Copy the code
Environment

Environment is another table besides metatable that can be associated with values of type Thread, function, and UserData. The environment is the equivalent of a namespace through which an object looks up accessible variables.

Objects can share the same environment:

  • andthreadThe associatedenvironmentReferred to asThe global enironment, which are other children created by the threadthreadAnd the nestedfunctionThe defaultenironment
  • And the LuafunctionThe associatedenvironmentIs that thefunctionThe nesting createdfunctionThe defaultenvironment
  • And CfunctionThe associatedenvironmentIt can only be accessed in C code, which is also created in this functionuserdataThe defaultenvironment
  • anduserdataThe associatedenvironmentThere is no special meaning for Lua code, it is just a way touserdataA more convenient way to attach data;

Lua code can use getFenv and setfenv to manipulate Lua functions and the running thread’s environment, while C function, The environments of userData and other threads can only be operated using the C API.

Closure
function count()
    local i = 0
    return function()
        i = i + 1
        return i
    end
end

local counter = count()
print(counter())
print(counter())

-- partial function
function bind(val, f)
    return function(...)
        return f(val, ...)
    end
end

prt = bind("hello", print)
prt(10, 20)
Copy the code
Coroutine

Lua natively supports coroutines, which are represented by the Coroutine type. Lua coroutines represent lines that have independently executed processes. Coroutines are scheduled by the Lua interpreter, and only after one coroutine explicitly cedes execution can other coroutines be scheduled to execute.

  • coroutine.create– Creates a coroutine. This function acceptsfunctionType parameter, as the main function of the new coroutine;
  • coroutine.resume– Resuming coroutine execution. After this function is called on the newly created coroutine, the coroutine actually starts running, at which point,coroutine.resumeIs used as an argument to the coroutine main function. The coroutine will run until it callscoroutine.yieldVoluntarily relinquish executive power.coroutine.resumeFunctions are returned only after the coroutine main function completes execution, throws an exception, or voluntarily cedes control:
    • When the coroutine main function normally exits,coroutine.resumereturntrueAnd the return value of the main function;
    • When the coroutine main function exits unexpectedly,coroutine.resumereturnfalseAnd error messages;
    • Coroutine calledcoroutine.yieldWhen delegating the power of execution,coroutine.resumereturntruecoroutine.yieldCall parameter of;
  • coroutine.yield– Coroutine cedes execution rights. usecoroutine.resumeThe coroutine that voluntarily relinquish execution can resume execution from where it left off, at which point,coroutine.resumeThe parameter ofcoroutine.yieldThe return value of.
  • coroutine.wrap– Another way to create coroutines. This call returns a function that is equivalent to an explicit callcoroutine.resume. And this function andcoroutine.resumeThe difference is that when an exception is thrown in a coroutine, this function reposts the exception to the function caller (so,coroutine.resumeThe first return value of the function used to indicate whether the coroutine was successfully executed is also returned bycoroutine.wrapIgnored).
  • coroutine.running()– Returns the running coroutine. If the caller ismain thread, it will returnnil
  • coroutine.status()– Gets the current state of the coroutine, returning a string. The states of coroutines are:running.suspended.normal.dead

Code organization

Module

Lua’s modules work like any other language, storing a set of functions and constants with similar functions to make it easier for users to share code.

<Programming in Lua 2nd> From the user point of view, a *module* is a library that can be loaded through ``require`` and that defines one single global name containing a Table. Everything that the Module exports, such as functions and constants, it defines inside this table, which works as a namespace. A well-behaved module also arrange for ``require`` to return this table.Copy the code

The Lua implementation provides standard modules such as Math, IO, String, and so on that users can use directly in their code. Lua also provides users with a mechanism and method to implement custom modules, which can be developed using Lua code or C API.

Definition module

There are generally two ways to define modules using Lua code:

  • Module (), provided in Lua 5.1, simplifies the process of creating standard Modules in Lua.

    ``module(name, ...) `` creates a table and sets it as the value of the global ``name`` and the value of ``package.loaded[name]``, so that ``require`` returns it.Copy the code
    • The module() function actually does the following [4] :

      -- testm.lua (somewhere on the Lua path)
      local print = print
      module("testm")
      
      function export1(s)
          print(s)
      end
      
      function export2(s)
          export1(s)
      end
      
      -- muser.lua
      local testm = require("testm")
      testm.export2("text")
      Copy the code
      1. First of all,moduleConstruct one for the subsequent functiontablethetableWill berequireReturned as a return value to the caller;
      2. Secondly, thetableLet’s say theta is theta for these functionsenvrionmentThese functions within the module are not needed when calling each othertestmAs a prefix; At the same time, the global environment is overwritten by the environment.
      3. In addition, if usedpackage.seeallAs amoduleWhen,moduleWill thetablemetatable__indexMember is set to the global environment_G, the functions defined in this module can access the variables or functions of the global environment;
      4. Setting global variablestestmIs newly createdtable
      5. Set up thepackage.loaded["testm"]Is newly createdtable
    • If you use the varargs expression for module, that is, module(…) Lua uses the file name of the module as the module name, so the module file can be easily moved.

    • Lua 5.2+ no longer recommends using the module function to create modules. It has several drawbacks:

      • The package.seeall parameter exposes the global environment to the module environment. Users can use the module to query the global environment, for example, testm. IO, causing leaky Encapsulation problems.

      • The module function puts the table it creates into the global environment; It also automatically imports dependencies of the module into the global environment.

        module "hello.world" creates a table ``hello`` (if not already
        present) and ``world`` as a table within that. If ``hello.world``
        requires ``fred`` then ``fred`` becomes automatically available to
        all users of ``hello.world``, who may come to depend on this
        implementation detail and get confused if it changed.
        Copy the code
  • You are advised to use the following method to create modules in Lua 5.2 (Lua 5.1 also supports this method) :

    -- mod.lua
    local M = {}
    
    function M.answer()
        return 42
    end
    
    function M.show()
        print (M.answer())
    end
    
    return M
    Copy the code
Use the module

Lua provides built-in modules that are preloaded by the interpreter into the global environment and can be used directly in Lua code or referenced through the global environment.

-- main.lua
 for line in io.lines "myfile" do
     ...
 end

 -- or
 for line in _G.io.lines "myfile" do
     ...
 end
Copy the code

Before use, user-defined modules need to be loaded into the environment directly accessed by code blocks through the require function.

When a module is loaded, it will put the module into the global environment _G, and the loaded module can be called directly like the built-in module.

-- main.lua
require "socket"
socket.connect(...)
Copy the code

Or,

-- main.lua
local socklib = require "socket"
socklib.connect(...)
Copy the code

The above mentioned Lua 5.2 method implementation module does not define variables in the global environment. When using this type of module, you can only use the module via require:

-- main.lua
local mymod = require "mymod"
mymod.do_something()
Copy the code

In conclusion, the suggestions for using modules are as follows:

The ' 'require "name"' 'syntax was The one introduced in Lua 5.1; This call does not always return the module, but it was expected a global would be created with the name of the library (so, you now have a ``_G.name`` to use the library with). In new code, you should use ``local name = require "name"`` syntax;  it works in the vast majority of cases, but if you're working with some older modules. They may not support it, and you'll have to just use ``require "module"``.Copy the code
Module to find

The require function is responsible for finding and loading modules as follows (take require “testm” as an example) :

  1. According to thepackage.preload["testm"]Value of, judgetestmHas the module been loaded: If the module has been loaded,requirepackage.preload["testm"]Returns the value of If the module has not been loaded, proceed to Step 2.
  2. Call them one by onepackage.loadersSet up thesearcherFunction that can be used for loadingtestmloader. Lua provides four by defaultsearcher
    • A searcher simply looks for a loader in the package.preload table.
    • A searcher looks for a loader as a Lua library using package.path.
    • A searcher looks for a loader as a C library, using package.cpath.
    • A searcher searches the C path for a library for the root name of the given module.
  3. callloaderLoad and execute module code. ifloaderThere is a return value,requireAssign the return value topackage.preload["testm"]; ifloaderThere is no return value,requirepackage.loaded["testm"]The assignment fortrue
  4. requirepackage.loaded["testm"]Is returned to the caller;

The simulation code for the above process is as follows:

function require(name)
    if not package.loaded[name] then
        local loader = findloader(name)
        if loader == nil then
            error("unable to load module " .. name)
        end
        package.loaded[name] = true
        local res = loader(name)
        if res ~= nil then
            package.loaded[name] = res
        end
    end
    return package.loaded[name]
end
Copy the code

As you can see from the description above, require looks for Lua modules based on the path set in package.path and C modules based on the path mode set in package.cpath. Is the path mode included? And; String,; The path used to separate file systems,? Will be replaced by the module name by require. Such as:

-- package.path ./? .lua; The/usr/share/lua / 5.1 /? .lua; The/usr/share/lua / 5.1 /? /init.luaCopy the code

When require(“testm”) is executed, require looks for module code using the following path in turn:

/ usr/share /. / testm. Lua lua / 5.1 / testm. / usr/share/lua lua / 5.1 / testm/init. LuaCopy the code
Package (package)

Lua allows lines to organize modules in a hierarchy separated by. For example, module mod.sub is a submodule of module mod. A package is a collection of modules organized in this way, and it is also a unit in Lua for code distribution.

Similar to modules, for example, when using require to find and load submodule A.B.C, require determines whether the submodule has already been loaded by the value of package.loaded[” A.B.C “]. In contrast to modules, require calls. If submodules have not been loaded before. Convert to an operating system path separator. For example, on Unix-like platforms, A.B.C is converted to a/ B /c, and then a/b/c is used instead of? In package.path and package.cpath. After, look for submodule files.

The module function also provides support for submodules, for example, which can be defined as module(” A.B.C “). At the same time, module defines the global variable A.B.C to reference submodules:

``module`` puts the environment table into variable ``a.b.c``, that is ,
into a field ``c`` of a table in field ``b`` of a table ``a``. If any of
these intermediate tables do not exist, ``module`` creates them. Otherwise,
it reuses them.
Copy the code

It is important to note that there is no explicit correlation between sub-modules in the same package, except that their environments can be nested as mentioned above. For example, require(“a”) does not automatically mount its submodule a.b; A module is not automatically loaded when require(“a.b”) is executed;

object-oriented

Lua does not provide native support for the object-oriented programming model, but it does provide mechanisms such as the table type and metatable and environment that can be used to implement similar object-oriented functionality.

Here is an example of code from PiL:

--- A base class Account = {balance= 0} -- Lua hide `self` when using *colon operator*, a syntactic sugar function Account:new(o) -- A hidden `self` refers to table `Account` o = o or {} setmetable(o, self) self.__index = self return o end function Account:deposit(v) self.balance = self.balance + v end function Account.withdraw(self, v) if v > self.balance then error "insufficient funds" end self.balance = self.balance - v end -- creates an instance of Account a = Account:new{balance = 0} a:deposit(100.00) -- syntactic sugar of 'a. dreposit (a, Inheritance -- 'SpecialAccount' is just an instance of 'Account' up to now. SpecialAccount = Account:new() S = SpecialAccount:new{limit=1000.00} -- 'self' refers to 'SpecialAcount' -- the metatable of 's' is' SpecialAcccount '. -- `s` is a table and Lua cannot find a `deposit` field in it, so it look -- into `SpecialAccount`; it cannot find a `deposit` field there, too, So -- it looks into 'Account' and there it finds the original implementation -- for a 'deposit' s:deposit(100.00) -- What makes a `SpecialAccount` special is that we can redefine any method -- inherited from its superclass. function SpecialAccount:withdraw(v) if v - self.balance >= self:getLimit() then error"insufficient funds" end self.balance = self.balance - v end function SpecialAccount:getLimit() return self.limit or 0 end -- Lua does not go to `Account`, Because it finds the new 'withdraw' method -- in 'SpecialAccount' first.S: Withdraw (200.00)Copy the code

Due to language constraints, object-oriented simulation using Lua does not provide a privacy control mechanism.

Language interoperation

Luafaq# T4.4 luafaq# T4.5 luafaq# T7 has

C API

TODO: To be finished.

FFI
  • LuaJIT FFI
  • luaffi
  • Alien

other

Command line arguments

When a Lua script file is run with the Lua interpreter, the Lua interpreter passes all command line arguments to the script file through the global array arg of type TABLE:

The following command line call,

% lua -la b.lua t1 t2
Copy the code

Lua creates an ARG array with the following elements:

arg = {
    [-2]= "lua", [-1]= "-la",
    [0]= "b.lua", [1]= "t1", [2]= "t2"
}
Copy the code

The element whose index value is 0 is the script file name, the element whose index value starts from 1 is the command line parameter after the script file name that appears in the command, and the element whose index value is less than 0 is the command line parameter that appears before the script file name.

In Lua code, you can also use… The varargs expression gets a command-line argument whose index starts at 1.

Not surprisingly, Lua doesn’t provide a standard way to handle command-line arguments. However, developers can refer to the processing logic used by other Lua programs, such as Luarocks, or use the non-standard library LApp.

The following code is taken from Luarocks, which uses Lua’s string matching function for command-line argument parsing:

--- Extract flags from an argument list. -- Given string arguments, extract flag arguments into a flags set. -- For example, given "foo", "--tux=beep", "--bla", "bar", "--baz", -- it would return the following: -- {["bla"] = true, ["tux"] = "beep", ["baz"] = True}, "foo", "bar". function parse_flags(...) local args = {... } local flags = {} for i = #args, 1, -1 do local flag = args[i]:match("^%-%-(.*)") if flag then local var, val = flag:match("([a-z_%-]*)=(.*)") if val then flags[var] = val else flags[flag] = true end table.remove(args, i) end end return flags, unpack(args) endCopy the code

A decorator

Lua-users.org/wiki/Decora…

Optimization Suggestions

The first question is, do you actually have a problem? Is the program not *fast enough? Remember the three basic requirements of a sytem: Correct, Robust, and Efficient, and the engineering rule of thumb that you may have to pick only two. Donald Knuth is often quoted about optimisation: "If you optimise everything, you will always be unhappy" and "we should forget about small efficiencies, say about 97% of the time: premature optimisation is the root of all evil." Assume a program is correct and (hopefully) robust. There is a definite  cost in optimising that program, both in programmer time and in code readability. If you don't know what the slow bits are, then you will waste time making your ugly and maybe a little faster (which is why he says unhappy). <Lua Performance Tips> Nevertheles, we all know that performance is a key ingredient of programming. It is not by change that problems with exponential time  complexity are called *intractable*. A too late result is a useless result. So every good programmer should always balance the costs from spending resources to optimize a piece of code against the gains of saving resources when running  that code. The first question regarding optimization a good programmer always asks is: "Does the program needs to be optimized?" If the answer is positive (but only then), the second question should be: "Where?"Copy the code

So, the first rule of optimization advice is don’t try to optimize. If you do need to optimize, you need to use tools to locate the areas that need to be optimized, such as the frequently called and poorly performing functions in the code and the inefficient operations in the inner loop, etc., where the optimization can result in an overall performance improvement with less work. LuaProfiler is a tool for locating inefficient hot spots in code.

We could also use LuaJIT to run code instead of the standard Lua (Vanilla Lua), which could result in a performance improvement of tens of times.

Cpu-intensive operations can be placed in modules implemented using the C API. If implemented correctly, the overall performance can be similar to that of native C programs. At the same time, because Lua syntax is concise, the overall code is shorter and easier to maintain. In addition, LuaJIT provides FFI and similar interfaces that allow direct access to C functions and data structures provided by external libraries, eliminating the need to write modules using C apis.

Here are a few development tips to improve code performance:

  • Locals are faster than globals.

    Local variables are very fast as they reside in virtual machine
    registers, and are accessed directly by index. Global variables on the
    other hand, reside in a lua table and as such are accessed by a hash
    lookup.
    -- Thomas Jefferson
    Copy the code
    local next = next
    local i, v = next(t, nil)         -- 10% faster
    while i do i, v = next(t, i) end
    Copy the code
  • Memory allocation from the heap — e.g. repeatedly creating tables or closures — can slow things down.

  • X *0.5 is faster than division x/2; x*x is faster than x^2;

  • Try to avoid creating new strings;

  • Reuse objects as much as possible;

  • Use table.concat instead of.. Complete string concatenation;

  • Memoizing intermediate results that will be used multiple times Move calculations that are irrelevant to the loop outside the loop;

    function memoize(f)
        local mem = {}                    -- memoizing table
        setmetatable(mem, {__mode= "kv"}) -- make it weak
        return function(x)                -- new version of 'f', with
                                          -- memoizing
            local r = mem[x]
            if r == nil then              -- no previous result?
                r = f(x)                  -- calls original function
                mem[x] = r                -- store result for reuse
            end
            return r
        end
    end
    
    -- redefine 'loadstring'
    loadstring = memoize(loadstring)
    -- then use new version 'loadstring' as the original one
    Copy the code
  • Lua code compilation is a lot of work, so avoid compiling at run time (for example, calling loadString);

  • Lua table is divided into an array part and a dictionary part. When the table space is insufficient, inserting new elements triggers the rehash operation of the table to apply for more memory and reinsert the original elements. The overhead of rehashing becomes less significant as the number of inserts increases, such as three rehashes for inserting three elements into the array portion of an empty table, and only 20 rehashes for inserting millions of elements. But if you create many tables with fewer elements, this overhead becomes apparent. For tables, the most straightforward optimization is to pre-allocate memory when the table is created as needed:

    • It can be provided through the C APIlua_createtableFunction specifies the required space at table creation time;
    • Use placeholders:{true, true, true}Tells Lua to create an array that can hold three elementstable{x= 1, y= 2, z= 3}It has a similar effect.
  • As we know from the above description, Lua rehashes the table when it runs out of space and inserts new elements. This means that deleting the table element (setting the element value to nil) does not immediately trigger table memory reclamation, which will be completed on the next rehash. Therefore, to free the memory occupied by the table, it is best to delete the table itself.

  • Adjust the garbage collector configuration parameters for different usage scenarios.

    <Lua Performance Tips>:
    
    Most recycling in Lua is done automatically by the garbage collector. Lua
    uses an incremental garbage collector. That means that the collector
    performs its task in small steps (incrementally) interleaved with the
    program execution. The pace of these steops is proportional to memory
    allocation: for each amount of memory allocated by Lua, the garbage
    collector does some proportional work. The faster the program consumes
    memory, the faster the collector tries to recycle it.
    
    Function ``collectgarbage`` provides several functionalities: it may stop
    and restart the collector, force a full collection cycle, force a
    collection step, get the total memory in use by Lua, and change two
    parameters that affect the pace of the collector.
    
        * ``parse`` - controls how long the collector waits between finishing
          a collection cycle and starting the next one.
        * ``stepmul`` - controls how much work the collector does in each
          step.
    
    Roughly, smaller parses and larger step multipliers increase the
    collector's speed.
    Copy the code
    • For batch type programs, because the process life cycle is short, the need for garbage collection is not high, it can be closed;

    • For non-batch programs, you can’t simply turn off garbage collection and get away with it. However, you can temporarily stop garbage collection while doing time-sensitive logic. Garbage collection can be disabled when necessary and invoked explicitly when appropriate.

      In Lua 5.1, each time you force some collection when the collector is
      stopped, it automatically restarts. So, to keep it stopped, you must
      call ``collectgarbage("stop")`` immediately after forcing some
      collection.
      Copy the code
    • Adjust the garbage collector parameters as required. Faster garbage collection logic consumes more CPU, but reduces overall memory usage.

Commonly used class library

Libraries and Bindings Kepler Project

  • Pure Lua implementation of data processing, functional programming and operating system path operations and other functions of the library Penlight
  • PCRE compatible re library lrexlib
  • Binary string manipulation library struct
  • Socket, HTTP, Mail LuaSocket
  • Config file parsing library pl.config
  • Lexical scanner pl. Lexer
  • File system operation luafilesystem
  • Daemon luadaemon
  • Asynchronous network library Copas
  • Gevent-like asynchronous networking library Levent
  • Libuv Binding luv
  • QT Binding lqt
  • GTK Binding lua-gtk

The resources

  • The official manual
  • Programming in Lua 2nd Edition
  • The official FAQ
  • Unofficial FAQ
  • Cloud wind’s blog
  • lua-users FAQ
  • lua-users Lua Gotchas
  • awesome-lua
  • Learning Lua
  • Lua Tutorial
  • Learn Lua in 15 Minutes
  • Masterminds of Programming: Conversations with the Creators of Major Programming LanguageThere are interviews with Lua’s authors,Cloud windThe interview was conductedtranslation ;
  • Lua: Good, bad, and ugly parts

Footnotes

[1] En.wikipedia.org/wiki/Scope_…)#Lexical_scoping_vs._dynamic_scoping
[2] www.lua.org/manual/5.1/…
[3] Lua-users.org/wiki/LocalB…
[4] www.luafaq.org/#T1.37
[5] Lua-users.org/wiki/String…