跟着 Lua 5.1 官方参考文档学习 Lua (3)

文章目录

    • [2.5 -- Expressions](#2.5 – Expressions)
      • [2.5.1 -- Arithmetic Operators](#2.5.1 – Arithmetic Operators)
      • [2.5.2 -- Relational Operators](#2.5.2 – Relational Operators)
      • [2.5.3 -- Logical Operators](#2.5.3 – Logical Operators)
      • [2.5.4 -- Concatenation](#2.5.4 – Concatenation)
      • [2.5.5 -- The Length Operator](#2.5.5 – The Length Operator)
      • [2.5.6 -- Precedence](#2.5.6 – Precedence)
      • [2.5.7 -- Table Constructors](#2.5.7 – Table Constructors)
      • [2.5.8 -- Function Calls](#2.5.8 – Function Calls)
      • [2.5.9 -- Function Definitions](#2.5.9 – Function Definitions)
    • [2.6 -- Visibility Rules](#2.6 – Visibility Rules)

2.5 -- Expressions

The basic expressions in Lua are the following:

	exp ::= prefixexp
	exp ::= nil | false | true
	exp ::= Number
	exp ::= String
	exp ::= function
	exp ::= tableconstructor
	exp ::= `...´
	exp ::= exp binop exp
	exp ::= unop exp
	prefixexp ::= var | functioncall | `(´ exp `)´

Numbers and literal strings are explained in §2.1;

variables are explained in §2.3;

function definitions are explained in §2.5.9;

function calls are explained in §2.5.8;

table constructors are explained in §2.5.7.

Vararg expressions, denoted by three dots ('...'), can only be used when directly inside a vararg function ; they are explained in §2.5.9.

Binary operators comprise arithmetic operators (see §2.5.1), relational operators (see §2.5.2), logical operators (see §2.5.3), and the concatenation operator (see §2.5.4).

Unary operators comprise the unary minus (see §2.5.1), the unary not (see §2.5.3), and the unary length operator (see §2.5.5).

Both function calls and vararg expressions can result in multiple values. If an expression is used as a statement (only possible for function calls (see §2.4.6)), then its return list is adjusted to zero elements, thus discarding all returned values. If an expression is used as the last (or the only) element of a list of expressions, then no adjustment is made (unless the call is enclosed in parentheses). In all other contexts, Lua adjusts the result list to one element, discarding all values except the first one.

Here are some examples:

lua 复制代码
     f()                -- adjusted to 0 results
     g(f(), x)          -- f() is adjusted to 1 result
     g(x, f())          -- g gets x plus all results from f()
     a,b,c = f(), x     -- f() is adjusted to 1 result (c gets nil)
     a,b = ...          -- a gets the first vararg parameter, b gets
                        -- the second (both a and b can get nil if there
                        -- is no corresponding vararg parameter)
     
     a,b,c = x, f()     -- f() is adjusted to 2 results
     a,b,c = f()        -- f() is adjusted to 3 results
     return f()         -- returns all results from f()
     return ...         -- returns all received vararg parameters
     return x,y,f()     -- returns x, y, and all results from f()
     {f()}              -- creates a list with all results from f()
     {...}              -- creates a list with all vararg parameters
     {f(), nil}         -- f() is adjusted to 1 result

Any expression enclosed in parentheses always results in only one value. Thus, (f(x,y,z)) is always a single value, even if f returns several values. (The value of (f(x,y,z)) is the first value returned by f or nil if f does not return any values.)

2.5.1 -- Arithmetic Operators

Lua supports the usual arithmetic operators: the binary + (addition), - (subtraction), * (multiplication), / (division), % (modulo), and ^ (exponentiation); and unary - (negation). If the operands are numbers, or strings that can be converted to numbers (see §2.2.1), then all operations have the usual meaning. Exponentiation works for any exponent. For instance, x^(-0.5) computes the inverse of the square root of x. Modulo is defined as

     a % b == a - math.floor(a/b)*b

That is, it is the remainder of a division that rounds the quotient towards minus infinity.

2.5.2 -- Relational Operators

The relational operators in Lua are

     ==    ~=    <     >     <=    >=

These operators always result in false or true.

Equality (==) first compares the type of its operands. If the types are different, then the result is false. Otherwise, the values of the operands are compared.

Numbers and strings are compared in the usual way.

Objects (tables, userdata, threads, and functions) are compared by reference : two objects are considered equal only if they are the same object. Every time you create a new object (a table, userdata, thread, or function), this new object is different from any previously existing object.

You can change the way that Lua compares tables and userdata by using the "eq" metamethod (see §2.8).

The conversion rules of §2.2.1 do not apply to equality comparisons. Thus, "0"==0 evaluates to false , and t[0] and t["0"] denote different entries in a table.

The operator ~= is exactly the negation of equality (==).

The order operators work as follows. If both arguments are numbers, then they are compared as such. Otherwise, if both arguments are strings, then their values are compared according to the current locale. Otherwise, Lua tries to call the "lt" or the "le" metamethod (see §2.8). A comparison a > b is translated to b < a and a >= b is translated to b <= a.

2.5.3 -- Logical Operators

The logical operators in Lua are and , or , and not . Like the control structures (see §2.4.4), all logical operators consider both false and nil as false and anything else as true.

The negation operator not always returns false or true.

The conjunction operator and returns its first argument if this value is false or nil ; otherwise, and returns its second argument. 【0&0=0, 0&1=0】

The disjunction operator or returns its first argument if this value is different from nil and false ; otherwise, or returns its second argument. 【1|0=1, 1|1=1, 0|0=0, 0|1=1】

Both and and or use short-cut evaluation; that is, the second operand is evaluated only if necessary. Here are some examples:

lua 复制代码
     10 or 20            --> 10
     10 or error()       --> 10
     nil or "a"          --> "a"
     nil and 10          --> nil
     false and error()   --> false
     false and nil       --> false
     false or nil        --> nil
     10 and 20           --> 20

(In this manual, --> indicates the result of the preceding expression.)

2.5.4 -- Concatenation

The string concatenation operator in Lua is denoted by two dots ('..'). If both operands are strings or numbers, then they are converted to strings according to the rules mentioned in §2.2.1. Otherwise, the "concat" metamethod is called (see §2.8).

2.5.5 -- The Length Operator

The length operator is denoted by the unary operator #. The length of a string is its number of bytes (that is, the usual meaning of string length when each character is one byte).

The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil ; moreover, if t[1] is nil , n can be zero.

For a regular array, with non-nil values from 1 to a given n, its length is exactly that n, the index of its last value. If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).

补充:If you really need to handle arrays with holes up to their last index, you can use the function table.maxn, which returns the largest numerical positive index of a table.

The length operator provides several common Lua idioms:

lua 复制代码
print(a[#a]) -- prints the last value of list 'a'
a[#a] = nil -- removes this last value
a[#a+1] = v -- appends 'v' to the end of the list  

例子:从标准输入读取10行数据

lua 复制代码
a = {}
for i=1,10 do
    a[#a+1] = io.read()
end

例子:

lua 复制代码
t = {}
t[10000] = 1
print(#t) -- 长度为0,因为t[1]为nil

t = {}
t[1] = 1
t[2] = 1
t[3] = 1

t[10000] = 1
print(#t) -- 长度为3

print(table.maxn(t)) -- 输出 10000

2.5.6 -- Precedence

Operator precedence in Lua follows the table below, from lower to higher priority:

     or
     and
     <     >     <=    >=    ~=    ==
     ..
     +     -
     *     /     %
     not   #     - (unary)
     ^

As usual, you can use parentheses to change the precedences of an expression. The concatenation ('..') and exponentiation ('^') operators are right associative. All other binary operators are left associative.

例子:^ 和 ... 的右结合性

lua 复制代码
print(2^2^3) -- 2^(2^3) 而不是(2^2)^3

print("a" .. "b" .. "c") -- "a" .. ("b" .. "c") 而不是 ("a" .. "b") .. "c"

补充:

Therefore, the following expressions on the left are equivalent to those on the right:

lua 复制代码
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)

When in doubt, always use explicit parentheses.

2.5.7 -- Table Constructors

Table constructors are expressions that create tables. Every time a constructor is evaluated, a new table is created. A constructor can be used to create an empty table or to create a table and initialize some of its fields. The general syntax for constructors is

	tableconstructor ::= `{´ [fieldlist] `}´
	fieldlist ::= field {fieldsep field} [fieldsep]
	field ::= `[´ exp `]´ `=´ exp | Name `=´ exp | exp
	fieldsep ::= `,´ | `;´

Each field of the form [exp1] = exp2 adds to the new table an entry with key exp1 and value exp2. A field of the form name = exp is equivalent to ["name"] = exp. Finally, fields of the form exp are equivalent to [i] = exp, where i are consecutive numerical integers, starting with 1. Fields in the other formats do not affect this counting.

For example,

lua 复制代码
     a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }

is equivalent to

lua 复制代码
     do
       local t = {}
       t[f(1)] = g
       t[1] = "x"         -- 1st exp
       t[2] = "y"         -- 2nd exp
       t.x = 1            -- t["x"] = 1
       t[3] = f(x)        -- 3rd exp
       t[30] = 23
       t[4] = 45          -- 4th exp
       a = t
     end

If the last field in the list has the form exp and the expression is a function call or a vararg expression, then all values returned by this expression enter the list consecutively (see §2.5.8). To avoid this, enclose the function call or the vararg expression in parentheses (see §2.5).

The field list can have an optional trailing separator, as a convenience for machine-generated code.

2.5.8 -- Function Calls

A function call in Lua has the following syntax:

	functioncall ::= prefixexp args
	prefixexp ::= var | functioncall | `(´ exp `)´

In a function call, first prefixexp and args are evaluated. If the value of prefixexp has type function , then this function is called with the given arguments. Otherwise, the prefixexp "call" metamethod is called, having as first parameter the value of prefixexp, followed by the original call arguments (see §2.8).

The form

	functioncall ::= prefixexp `:´ Name args

can be used to call "methods". A call v:name(args) is syntactic sugar for v.name(v,args), except that v is evaluated only once.

Arguments have the following syntax:

	args ::= `(´ [explist] `)´
	args ::= tableconstructor
	args ::= String

All argument expressions are evaluated before the call.

A call of the form f{fields} is syntactic sugar for f({fields}); that is, the argument list is a single new table.

A call of the form f'string' (or f"string" or f[[string]]) is syntactic sugar for f('string'); that is, the argument list is a single literal string.

例子:函数参数语法的三种形式

lua 复制代码
function foo(a)
    print(a)
end

foo(1)   -- 1

foo"abc" -- abc

foo{1,2,3} -- table: ...

As an exception to the free-format syntax of Lua, you cannot put a line break before the '(' in a function call. This restriction avoids some ambiguities in the language. If you write

     a = f
     (g).x(a)

Lua would see that as a single statement, a = f(g).x(a). So, if you want two statements, you must add a semi-colon between them. If you actually want to call f, you must remove the line break before (g).

A call of the form return functioncall is called a tail call. Lua implements proper tail calls (or proper tail recursion): in a tail call, the called function reuses the stack entry of the calling function. Therefore, there is no limit on the number of nested tail calls that a program can execute. However, a tail call erases any debug information about the calling function.

Note that a tail call only happens with a particular syntax, where the return has one single function call as argument; this syntax makes the calling function return exactly the returns of the called function. So, none of the following examples are tail calls:

lua 复制代码
     return (f(x))        -- results adjusted to 1
     return 2 * f(x)
     return x, f(x)       -- additional results
     f(x); return         -- results discarded
     return x or f(x)     -- results adjusted to 1

2.5.9 -- Function Definitions

The syntax for function definition is

	function ::= function funcbody
	funcbody ::= `(´ [parlist] `)´ block end

The following syntactic sugar simplifies function definitions:

	stat ::= function funcname funcbody
	stat ::= local function Name funcbody
	funcname ::= Name {`.´ Name} [`:´ Name]

The statement

     function f () body end

translates to

     f = function () body end

The statement

     function t.a.b.c.f () body end

translates to

     t.a.b.c.f = function () body end

The statement

     local function f () body end

translates to

     local f; f = function () body end

not to

     local f = function () body end

(This only makes a difference when the body of the function contains references to f.)

补充:So, we can use this syntax for recursive functions without worrying:

lua 复制代码
local function fact (n)
	if n == 0 then return 1
	else return n*fact(n-1)
	end
end  

A function definition is an executable expression, whose value has type function.

When Lua pre-compiles a chunk, all its function bodies are pre-compiled too. Then, whenever Lua executes the function definition, the function is instantiated (or closed ). This function instance (or closure) is the final value of the expression. Different instances of the same function can refer to different external local variables and can have different environment tables.


补充:When a function is written enclosed in another function, it has full access to local variables from the enclosing function; this feature is called lexical scoping.

例子:闭包

lua 复制代码
names = {"Peter", "Paul", "Mary"}
grades = {Mary = 10, Paul = 7, Peter = 8}

function sortbygrade (names, grades)
    table.sort(names, function (n1, n2)
        return grades[n1] > grades[n2] -- compare the grades
    end)
end

sortbygrade(names, grades)

for _, v in ipairs(names) do
    print(v)
end

The interesting point in the example is that the anonymous function given to sort accesses the parameter grades , which is local to the enclosing function sortbygrade . Inside this anonymous function, grades is neither a global variable nor a local variable, but what we call a non-local variable . (For historical reasons, non-local variables are also called upvalues in Lua.)

例子:使用闭包实现计数器

lua 复制代码
function newCounter ()
    local i = 0
    return function () -- anonymous function
        i = i + 1
        return i
    end
end

c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2

c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2

In this code, the anonymous function refers to a non-local variable, i , to keep its counter. However, by the time we call the anonymous function, i is already out of scope, because the function that created this variable (newCounter ) has returned. Nevertheless, Lua handles this situation correctly, using the concept of closure . Simply put, a closure is a function plus all it needs to access nonlocal variables correctly.

Technically speaking, what is a value in Lua is the closure, not the function. The function itself is just a prototype for closures. Nevertheless, we will continue to use the term "function" to refer to a closure whenever there is no possibility of confusion.


Parameters act as local variables that are initialized with the argument values:

	parlist ::= namelist [`,´ `...´] | `...´

When a function is called, the list of arguments is adjusted to the length of the list of parameters, unless the function is a variadic or vararg function , which is indicated by three dots ('...') at the end of its parameter list.

A vararg function does not adjust its argument list; instead, it collects all extra arguments and supplies them to the function through a vararg expression, which is also written as three dots. The value of this expression is a list of all actual extra arguments, similar to a function with multiple results.

If a vararg expression is used inside another expression or in the middle of a list of expressions, then its return list is adjusted to one element. If the expression is used as the last element of a list of expressions, then no adjustment is made (unless that last expression is enclosed in parentheses).

As an example, consider the following definitions:

lua 复制代码
     function f(a, b) end
     function g(a, b, ...) end
     function r() return 1,2,3 end

Then, we have the following mapping from arguments to parameters and to the vararg expression:

     CALL            PARAMETERS
     
     f(3)             a=3, b=nil
     f(3, 4)          a=3, b=4
     f(3, 4, 5)       a=3, b=4
     f(r(), 10)       a=1, b=10
     f(r())           a=1, b=2
     
     g(3)             a=3, b=nil, ... -->  (nothing)
     g(3, 4)          a=3, b=4,   ... -->  (nothing)
     g(3, 4, 5, 8)    a=3, b=4,   ... -->  5  8
     g(5, r())        a=5, b=1,   ... -->  2  3

Results are returned using the return statement (see §2.4.4). If control reaches the end of a function without encountering a return statement, then the function returns with no results.

The colon syntax is used for defining methods , that is, functions that have an implicit extra parameter self. Thus, the statement

     function t.a.b.c:f (params) body end

is syntactic sugar for

     t.a.b.c.f = function (self, params) body end

2.6 -- Visibility Rules

Lua is a lexically scoped language. The scope of variables begins at the first statement after their declaration and lasts until the end of the innermost block that includes the declaration. Consider the following example:

lua 复制代码
     x = 10                -- global variable
     do                    -- new block
       local x = x         -- new 'x', with value 10
       print(x)            --> 10
       x = x+1
       do                  -- another block
         local x = x+1     -- another 'x'
         print(x)          --> 12
       end
       print(x)            --> 11
     end
     print(x)              --> 10  (the global one)

Notice that, in a declaration like local x = x, the new x being declared is not in scope yet, and so the second x refers to the outside variable.

Because of the lexical scoping rules, local variables can be freely accessed by functions defined inside their scope. A local variable used by an inner function is called an upvalue , or external local variable, inside the inner function.

Notice that each execution of a local statement defines new local variables. Consider the following example:

     a = {}
     local x = 20
     for i=1,10 do
       local y = 0
       a[i] = function () y=y+1; return x+y end
     end

The loop creates ten closures (that is, ten instances of the anonymous function). Each of these closures uses a different y variable, while all of them share the same x.

相关推荐
阿湯哥11 小时前
Lua脚本核心语法介绍
开发语言·junit·lua
王小义笔记21 小时前
Postman如何流畅使用DeepSeek
开发语言·测试工具·lua·postman·deepseek
程序猿多布2 天前
数学函数(C#、Lua 、Unity)
unity·c#·lua
程序猿多布2 天前
字符串操作总结(C# and Lua)
c#·lua
浅陌sss2 天前
Xlua中C#引用Lua变量,导致Lua侧的GC无法回收的原因及解决方法
c#·lua
张胤尘2 天前
Lua | 面试题每日一练 (1)
开发语言·后端·lua
a小胡哦3 天前
从入门到精通:Postman 实用指南
测试工具·lua·postman
alenliu06213 天前
跟着 Lua 5.1 官方参考文档学习 Lua (4)
lua
张胤尘3 天前
Lua | 每日一练 (2)
开发语言·面试·lua
alenliu06214 天前
跟着 Lua 5.1 官方参考文档学习 Lua (1)
lua