📄 lua 5_1 参考手册.htm
字号:
return ... -- 返回所有从可变参数中接收来的值
return x,y,f() -- 返回 x, y, 以及所有 f() 的返回值
{f()} -- 用 f() 的所有返回值创建一个列表
{...} -- 用可变参数中的所有值创建一个列表
{f(), nil} -- f() 被调整为一个结果
</PRE>
<P>被括号括起来的表达式永远被当作一个值。所以, <CODE>(f(x,y,z))</CODE> 即使 <CODE>f</CODE>
返回多个值,这个表达式永远是一个单一值。(<CODE>(f(x,y,z))</CODE> 的值是 <CODE>f</CODE> 返回的第一个值。如果
<CODE>f</CODE> 不返回值的话,那么它的值就是 <B>nil</B> 。)
<H3>2.5.1 - <A name=2.5.1>数学运算操作符</A></H3>
<P>Lua 支持常见的数学运算操作符:二元操作 <CODE>+</CODE> (加法), <CODE>-</CODE> (减法),<CODE>*</CODE>
(乘法), <CODE>/</CODE> (除法), <CODE>%</CODE> (取模),以及 <CODE>^</CODE> (幂);和一元操作
<CODE>-</CODE> (取负)。如果对数字操作,或是可以转换为数字的字符串(参见 <A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.2.1">§2.2.1</A>),所有这些操作都依赖它通常的含义。幂操作可以对任何幂值都正常工作。比如,
<CODE>x^(-0.5)</CODE> 将计算出 <CODE>x</CODE> 的平方根。取模操作被定义为 <PRE> a % b == a - math.floor(a/b)*b
</PRE>
<P>这就是说,其结果是商相对负无穷圆整后的余数。(译注:负数对正数取模的结果为正数)
<H3>2.5.2 - <A name=2.5.2>比较操作符</A></H3>
<P>Lua 中的比较操作符有 <PRE> == ~= < > <= >=
</PRE>
<P>这些操作的结果不是 <B>false</B> 就是 <B>true</B>。
<P>等于操作 (<CODE>==</CODE>) 首先比较操作数的类型。如果类型不同,结果就是
<B>false</B>。否则,继续比较值。数字和字符串都用常规的方式比较。对象 (table ,userdata ,thread
,以及函数)以引用的形式比较:两个对象只有在它们指向同一个东西时才认为相等。每次你创建一个新对象(一个 table 或是 userdata ,thread
函数),它们都各不相同,即不同于上次创建的东西。
<P>你可以改变 Lua 比较 table 和 userdata 的方式,这需要使用 "eq" 这个原方法(参见 <A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.8">§2.8</A>)。
<P><A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.2.1">§2.2.1</A>
中提及的转换规则并不作用于比较操作。所以, <CODE>"0"==0</CODE> 等于 <B>false</B>,而且 <CODE>t[0]</CODE> 和
<CODE>t["0"]</CODE> 描述的是 table 中不同的域。
<P>操作符 <CODE>~=</CODE> 完全等价于 (<CODE>==</CODE>) 操作的反值。
<P>大小比较操作以以下方式进行。如果参数都是数字,那么就直接做数字比较。否则,如果参数都是字符串,就用字符串比较的方式进行。再则,Lua 就试着调用 "lt"
或是 "le" 元方法(参见 <A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.8">§2.8</A>)。
<H3>2.5.3 - <A name=2.5.3>逻辑操作符</A></H3>
<P>Lua 中的逻辑操作符有 <B>and</B>, <B>or</B>, 以及 <B>not</B>。和控制结构(参见 <A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.4.4">§2.4.4</A>)一样,所有的逻辑操作符把
<B>false</B> 和 <B>nil</B> 都作为假,而其它的一切都当作真。
<P>取反操作 <B>not</B> 总是返回 <B>false</B> 或 <B>true</B> 中的一个。与操作符 <B>and</B> 在第一个参数为
<B>false</B> 或 <B>nil</B> 时返回这第一个参数;否则,<B>and</B> 返回第二个参数。或操作符 <B>or</B>
在第一个参数不为 <B>nil</B> 也不为 <B>false</B> 时,返回这第一个参数,否则返回第二个参数。 <B>and</B> 和
<B>or</B> 都遵循短路规则;也就是说,第二个操作数只在需要的时候去求值。这里有一些例子: <PRE> 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
</PRE>
<P>(在这本手册中, --> 指前面表达式的结果。)
<H3>2.5.4 - <A name=2.5.4>连接符</A></H3>
<P>Lua 中字符串的连接操作符写作两个点 ('<CODE>..</CODE>')。如果两个操作数都是字符串或都是数字,连接操作将以 <A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.2.1">§2.2.1</A>
中提到的规则把其转换为字符串。否则,会取调用元方法 "concat" (参见 <A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.8">§2.8</A>)。
<H3>2.5.5 - <A name=2.5.5>取长度操作符</A></H3>
<P>取长度操作符写作一元操作 <CODE>#</CODE>。字符串的长度是它的字节数(就是以一个字符一个字节计算的字符串长度)。
<P>table <CODE>t</CODE> 的长度被定义成一个整数下标 <CODE>n</CODE> 。它满足 <CODE>t[n]</CODE> 不是
<B>nil</B> 而 <CODE>t[n+1]</CODE> 为 <B>nil</B>;此外,如果 <CODE>t[1]</CODE> 为
<B>nil</B> ,<CODE>n</CODE> 就可能是零。对于常规的数组,里面从 1 到 <CODE>n</CODE>
放着一些非空的值的时候,它的长度就精确的为 <CODE>n</CODE>,即最后一个值的下标。如果数组有一个“空洞” (就是说,<B>nil</B>
值被夹在非空值之间),那么 <CODE>#t</CODE> 可能是任何一个是 <B>nil</B> 值的位置的下标(就是说,任何一个 <B>nil</B>
值都有可能被当成数组的结束)。
<H3>2.5.6 - <A name=2.5.6>优先级</A></H3>
<P>Lua 中操作符的优先级写在下表中,从低到高优先级排序: <PRE> or
and
< > <= >= ~= ==
..
+ -
* / %
not # - (unary)
^
</PRE>
<P>通常,你可以用括号来改变运算次序。连接操作符 ('<CODE>..</CODE>') 和幂操作 ('<CODE>^</CODE>')
是从右至左的。其它所有的操作都是从左至右。
<H3>2.5.7 - <A name=2.5.7>Table 构造</A></H3>
<P>table 构造子是一个构造 table 的表达式。每次构造子被执行,都会构造出一个新的 table 。构造子可以被用来构造一个空的
table,也可以用来构造一个 table 并初始化其中的一些域。一般的构造子的语法如下 <PRE> tableconstructor ::= `<B>{</B>´ [fieldlist] `<B>}</B>´
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= `<B>[</B>´ exp `<B>]</B>´ `<B>=</B>´ exp | Name `<B>=</B>´ exp | exp
fieldsep ::= `<B>,</B>´ | `<B>;</B>´
</PRE>
<P>每个形如 <CODE>[exp1] = exp2</CODE> 的域向 table 中增加新的一项,其键值为 <CODE>exp1</CODE> 而值为
<CODE>exp2</CODE>。形如 <CODE>name = exp</CODE> 的域等价于 <CODE>["name"] =
exp</CODE>。最后,形如 <CODE>exp</CODE> 的域等价于 <CODE>[i] = exp</CODE> , 这里的
<CODE>i</CODE> 是一个从 1 开始不断增长的数字。这这个格式中的其它域不会破坏其记数。举个例子: <PRE> a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
</PRE>
<P>等价于 <PRE> 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
</PRE>
<P>如果表单中最后一个域的形式是 <CODE>exp</CODE>
,而且其表达式是一个函数调用或者是一个可变参数,那么这个表达式所有的返回值将连续的进入列表(参见 <A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.5.8">§2.5.8</A>)。为了避免这一点,你可以用括号把函数调用(或是可变参数)括起来(参见
<A href="http://www.codingnow.com/2000/download/lua_manual.html#2.5">§2.5</A>)。
<P>初始化域表可以在最后多一个分割符,这样设计可以方便由机器生成代码。
<H3>2.5.8 - <A name=2.5.8>函数调用</A></H3>
<P>Lua 中的函数调用的语法如下: <PRE> functioncall ::= prefixexp args
</PRE>
<P>函数调用时,第一步,prefixexp 和 args 先被求值。如果 prefixexp 的值的类型是
<EM>function</EM>,那么这个函数就被用给出的参数调用。否则 prefixexp 的元方法 "call" 就被调用,第一个参数就是
prefixexp 的值,跟下来的是原来的调用参数(参见 <A
href="http://www.codingnow.com/2000/download/lua_manual.html#2.8">§2.8</A>)。
<P>这样的形式 <PRE> functioncall ::= prefixexp `<B>:</B>´ Name args
</PRE>
<P>可以用来调用 "方法"。这是 Lua 支持的一种语法糖。像 <CODE>v:name(<EM>args</EM>)</CODE> 这个样子,被解释成
<CODE>v.name(v,<EM>args</EM>)</CODE>,这里 <CODE>v</CODE> 只会被求值一次。
<P>参数的语法如下: <PRE> args ::= `<B>(</B>´ [explist1] `<B>)</B>´
args ::= tableconstructor
args ::= String
</PRE>
<P>所有参数的表达式求值都在函数调用之前。这样的调用形式 <CODE>f{<EM>fields</EM>}</CODE> 是一种语法糖用于表示
<CODE>f({<EM>fields</EM>})</CODE>;这里指参数列表是一个单一的新创建出来的列表。而这样的形式
<CODE>f'<EM>string</EM>'</CODE> (或是 <CODE>f"<EM>string</EM>"</CODE> 亦或是
<CODE>f[[<EM>string</EM>]]</CODE>)也是一种语法糖,用于表示
<CODE>f('<EM>string</EM>')</CODE>;这里指参数列表是一个单独的字符串。
<P>因为表达式语法在 Lua 中比较自由,所以你不能在函数调用的 '<CODE>(</CODE>' 前换行。这个限制可以避免语言中的一些歧义。比如你这样写 <PRE> a = f
(g).x(a)
</PRE>
<P>Lua 将把它当作一个单一语句段, <CODE>a = f(g).x(a)</CODE>
。因此,如果你真的想作为成两个语句段,你必须在它们之间写上一个分号。如果你真的想调用 <CODE>f</CODE>,你必须从 <CODE>(g)</CODE>
前移去换行。
<P>这样一种调用形式:<CODE>return</CODE> <EM>functioncall</EM> 将触发一个尾调用。 Lua
实现了适当的尾部调用(或是适当的尾递归):在尾调用中,被调用的函数重用调用它的函数的堆栈项。因此,对于程序执行的嵌套尾调用的层数是没有限制的。然而,尾调用将删除调用它的函数的任何调试信息。注意,尾调用只发生在特定的语法下,这时,
<B>return</B> 只有单一函数调用作为参数;这种语法使得调用函数的结果可以精确返回。因此,下面这些例子都不是尾调用: <PRE> return (f(x)) -- 返回值被调整为一个
return 2 * f(x)
return x, f(x) -- 最加若干返回值
f(x); return -- 无返回值
return x or f(x) -- 返回值被调整为一个
</PRE>
<H3>2.5.9 - <A name=2.5.9>函数定义</A></H3>
<P>函数定义的语法如下: <PRE> function ::= <B>function</B> funcbody
funcbody ::= `<B>(</B>´ [parlist1] `<B>)</B>´ block <B>end</B>
</PRE>
<P>另外定义了一些语法糖简化函数定义的写法: <PRE> stat ::= <B>function</B> funcname funcbody
stat ::= <B>local</B> <B>function</B> Name funcbody
funcname ::= Name {`<B>.</B>´ Name} [`<B>:</B>´ Name]
</PRE>
<P>这样的写法: <PRE> function f () <EM>body</EM> end
</PRE>
<P>被转换成 <PRE> f = function () <EM>body</EM> end
</PRE>
<P>这样的写法: <PRE> function t.a.b.c.f () <EM>body</EM> end
</PRE>
<P>被转换成 <PRE> t.a.b.c.f = function () <EM>body</EM> end
</PRE>
<P>这样的写法: <PRE> local function f () <EM>body</EM> end
</PRE>
<P>被转换成 <PRE> local f; f = function () <EM>body</EM> end
</PRE>
<P>注意,并不是转换成 <PRE> local f = function () <EM>body</EM> end
</PRE>
<P>(这个差别只在函数体内需要引用 <CODE>f</CODE> 时才有。)
<P>一个函数定义是一个可执行的表达式,执行结果是一个类型为 <EM>function</EM> 的值。当 Lua 预编译一个 chunk 的时候, chunk
作为一个函数,整个函数体也就被预编译了。那么,无论何时 Lua 执行了函数定义,这个函数本身就被实例化了(或者说是关闭了)。这个函数的实例(或者说是
<EM>closure</EM>(闭包))是表达式的最终值。相同函数的不同实例有可能引用不同的外部局部变量,也可能拥有不同的环境表。
<P>形参(函数定义需要的参数)是一些由实参(实际传入参数)的值初始化的局部变量: <PRE> parlist1 ::= namelist [`<B>,</B>´ `<B>...</B>´] | `<B>...</B>´
</PRE>
<P>当一个函数被调用,如果函数没有被定义为接收不定长参数,即在形参列表的末尾注明三个点
('<CODE>...</CODE>'),那么实参列表就会被调整到形参列表的长度,变长参数函数不会调整实参列表;取而代之的是,它将把所有额外的参数放在一起通过变长参数表达式传递给函数,其写法依旧是三个点。这个表达式的值是一串实参值的列表,看起来就跟一个可以返回多个结果的函数一样。如果一个变长参数表达式放在另一个表达式中使用,或是放在另一串表达式的中间,那么它的返回值就会被调整为单个值。若这个表达式放在了一系列表达式的最后一个,就不会做调整了(除非用括号给括了起来)。
<P>我们先做如下定义,然后再来看一个例子: <PRE> function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
</PRE>
<P>下面看看实参到形参数以及可变长参数的映射关系: <PRE> CALL PARAMETERS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -