⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lua 5.1 参考手册.txt

📁 lua的参考文档请大家好好看看很不错
💻 TXT
📖 第 1 页 / 共 5 页
字号:
       t[30] = 23
       t[4] = 45          -- 4th exp
       a = t
     end

如果表单中最后一个域的形式是 exp ,而且其表达式是一个函数调用或者是一个可变参数,那么这个表达式所有的返回值将连续的进入列表(参见 §2.5.8)。为了避免这一点,你可以用括号把函数调用(或是可变参数)括起来(参见 §2.5)。 

初始化域表可以在最后多一个分割符,这样设计可以方便由机器生成代码。 

2.5.8 - 函数调用
Lua 中的函数调用的语法如下: 

	functioncall ::= prefixexp args

函数调用时,第一步,prefixexp 和 args 先被求值。如果 prefixexp 的值的类型是 function,那么这个函数就被用给出的参数调用。否则 prefixexp 的元方法 "call" 就被调用,第一个参数就是 prefixexp 的值,跟下来的是原来的调用参数(参见 §2.8)。 

这样的形式 

	functioncall ::= prefixexp `:′ Name args

可以用来调用 "方法"。这是 Lua 支持的一种语法糖。像 v:name(args) 这个样子,被解释成 v.name(v,args),这里 v 只会被求值一次。 

参数的语法如下: 

	args ::= `(′ [explist1] `)′
	args ::= tableconstructor
	args ::= String

所有参数的表达式求值都在函数调用之前。这样的调用形式 f{fields} 是一种语法糖用于表示 f({fields});这里指参数列表是一个单一的新创建出来的列表。而这样的形式 f'string' (或是 f"string" 亦或是 f[[string]])也是一种语法糖,用于表示 f('string');这里指参数列表是一个单独的字符串。 

因为表达式语法在 Lua 中比较自由,所以你不能在函数调用的 '(' 前换行。这个限制可以避免语言中的一些歧义。比如你这样写 

     a = f
     (g).x(a)

Lua 将把它当作一个单一语句段, a = f(g).x(a) 。因此,如果你真的想作为成两个语句段,你必须在它们之间写上一个分号。如果你真的想调用 f,你必须从 (g) 前移去换行。 

这样一种调用形式:return functioncall 将触发一个尾调用。 Lua 实现了适当的尾部调用(或是适当的尾递归):在尾调用中,被调用的函数重用调用它的函数的堆栈项。因此,对于程序执行的嵌套尾调用的层数是没有限制的。然而,尾调用将删除调用它的函数的任何调试信息。注意,尾调用只发生在特定的语法下,这时, return 只有单一函数调用作为参数;这种语法使得调用函数的结果可以精确返回。因此,下面这些例子都不是尾调用: 

     return (f(x))        -- 返回值被调整为一个
     return 2 * f(x)
     return x, f(x)       -- 最加若干返回值
     f(x); return         -- 无返回值
     return x or f(x)     -- 返回值被调整为一个

2.5.9 - 函数定义
函数定义的语法如下: 

	function ::= function funcbody
	funcbody ::= `(′ [parlist1] `)′ block end

另外定义了一些语法糖简化函数定义的写法: 

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

这样的写法: 

     function f () body end

被转换成 

     f = function () body end

这样的写法: 

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

被转换成 

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

这样的写法: 

     local function f () body end

被转换成 

     local f; f = function () body end

注意,并不是转换成 

     local f = function () body end

(这个差别只在函数体内需要引用 f 时才有。) 

一个函数定义是一个可执行的表达式,执行结果是一个类型为 function 的值。当 Lua 预编译一个 chunk 的时候, chunk 作为一个函数,整个函数体也就被预编译了。那么,无论何时 Lua 执行了函数定义,这个函数本身就被实例化了(或者说是关闭了)。这个函数的实例(或者说是 closure(闭包))是表达式的最终值。相同函数的不同实例有可能引用不同的外部局部变量,也可能拥有不同的环境表。 

形参(函数定义需要的参数)是一些由实参(实际传入参数)的值初始化的局部变量: 

	parlist1 ::= namelist [`,′ `...′] | `...′

当一个函数被调用,如果函数没有被定义为接收不定长参数,即在形参列表的末尾注明三个点 ('...'),那么实参列表就会被调整到形参列表的长度,变长参数函数不会调整实参列表;取而代之的是,它将把所有额外的参数放在一起通过变长参数表达式传递给函数,其写法依旧是三个点。这个表达式的值是一串实参值的列表,看起来就跟一个可以返回多个结果的函数一样。如果一个变长参数表达式放在另一个表达式中使用,或是放在另一串表达式的中间,那么它的返回值就会被调整为单个值。若这个表达式放在了一系列表达式的最后一个,就不会做调整了(除非用括号给括了起来)。 

我们先做如下定义,然后再来看一个例子: 

     function f(a, b) end
     function g(a, b, ...) end
     function r() return 1,2,3 end

下面看看实参到形参数以及可变长参数的映射关系: 

     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

结果由 return 来返回(参见 §2.4.4)。如果执行到函数末尾依旧没有遇到任何 return 语句,函数就不会返回任何结果。 

冒号语法可以用来定义方法,就是说,函数可以有一个隐式的形参 self。因此,如下写法: 

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

是这样一种写法的语法糖: 

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

2.6 - 可视规则
Lua 是一个有词法作用范围的语言。变量的作用范围开始于声明它们之后的第一个语句段,结束于包含这个声明的最内层语句块的结束点。看下面这些例子: 

     x = 10                -- 全局变量
     do                    -- 新的语句块
       local x = x         -- 新的一个 'x', 它的值现在是 10
       print(x)            --> 10
       x = x+1
       do                  -- 另一个语句块
         local x = x+1     -- 又一个 'x'
         print(x)          --> 12
       end
       print(x)            --> 11
     end
     print(x)              --> 10  (取到的是全局的那一个)

注意这里,类似 local x = x 这样的声明,新的 x 正在被声明,但是还没有进入它的作用范围,所以第二个 x 指向的是外面一层的变量。 

因为有这样一个词法作用范围的规则,所以可以在函数内部自由的定义局部变量并使用它们。当一个局部变量被更内层的函数中使用的时候,它被内层函数称作 upvalue(上值),或是 外部局部变量。 

注意,每次执行到一个 local 语句都会定义出一个新的局部变量。看看这样一个例子: 

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

这个循环创建了十个 closure(这指十个匿名函数的实例)。这些 closure 中的每一个都使用了不同的 y 变量,而它们又共享了同一份 x。 

2.7 - 错误处理
因为 Lua 是一个嵌入式的扩展语言,所有的 Lua 动作都是从宿主程序的 C 代码调用 Lua 库(参见 lua_pcall)中的一个函数开始的。在 Lua 编译或运行的任何时候发生了错误,控制权都会交还给 C ,而 C 可以来做一些恰当的措施(比如打印出一条错误信息)。 

Lua 代码可以显式的调用 error 函数来产生一条错误。如果你需要在 Lua 中捕获发生的错误,你可以使用 pcall 函数。 

2.8 - Metatable(元表)
Lua 中的每个值都可以用一个 metatable。这个 metatable 就是一个原始的 Lua table ,它用来定义原始值在特定操作下的行为。你可以通过在 metatable 中的特定域设一些值来改变拥有这个 metatable 的值的指定操作之行为。举例来说,当一个非数字的值作加法操作的时候, Lua 会检查它的 metatable 中 "__add" 域中的是否有一个函数。如果有这么一个函数的话,Lua 调用这个函数来执行一次加法。 

我们叫 metatable 中的键名为 事件 (event) ,把其中的值叫作 元方法 (metamethod)。在上个例子中,事件是 "add" 而元方法就是那个执行加法操作的函数。 

你可以通过 getmetatable 函数来查询到任何一个值的 metatable。 

你可以通过 setmetatable 函数来替换掉 table 的 metatable 。你不能从 Lua 中改变其它任何类型的值的 metatable (使用 debug 库例外);要这样做的话必须使用 C API 。 

每个 table 和 userdata 拥有独立的 metatable (当然多个 table 和 userdata 可以共享一个相同的表作它们的 metatable);其它所有类型的值,每种类型都分别共享唯一的一个 metatable。因此,所有的数字一起只有一个 metatable ,所有的字符串也是,等等。 

一个 metatable 可以控制一个对象做数学运算操作、比较操作、连接操作、取长度操作、取下标操作时的行为, metatable 中还可以定义一个函数,让 userdata 作垃圾收集时调用它。对于这些操作,Lua 都将其关联上一个被称作事件的指定健。当 Lua 需要对一个值发起这些操作中的一个时,它会去检查值中 metatable 中是否有对应事件。如果有的话,键名对应的值(元方法)将控制 Lua 怎样做这个操作。 

metatable 可以控制的操作已在下面列出来。每个操作都用相应的名字区分。每个操作的键名都是用操作名字加上两个下划线 '__' 前缀的字符串;举例来说,"add" 操作的键名就是字符串 "__add"。这些操作的语义用一个 Lua 函数来描述解释器如何执行更为恰当。 

这里展示的用 Lua 写的代码仅作解说用;实际的行为已经硬编码在解释器中,其执行效率要远高于这些模拟代码。这些用于描述的的代码中用到的函数( rawget , tonumber ,等等。)都可以在 §5.1 中找到。特别注意,我们使用这样一个表达式来从给定对象中提取元方法 

     metatable(obj)[event]

这个应该被解读作 

     rawget(getmetatable(obj) or {}, event)

这就是说,访问一个元方法不再会触发任何的元方法,而且访问一个没有 metatable 的对象也不会失败(而只是简单返回 nil)。 

"add": + 操作。 
下面这个 getbinhandler 函数定义了 Lua 怎样选择一个处理器来作二元操作。首先,Lua 尝试第一个操作数。如果这个东西的类型没有定义这个操作的处理器,然后 Lua 会尝试第二个操作数。 

     function getbinhandler (op1, op2, event)
       return metatable(op1)[event] or metatable(op2)[event]
     end

通过这个函数, op1 + op2 的行为就是 

     function add_event (op1, op2)
       local o1, o2 = tonumber(op1), tonumber(op2)
       if o1 and o2 then  -- 两个操作数都是数字?
         return o1 + o2   -- 这里的 '+' 是原生的 'add'
       else  -- 至少一个操作数不是数字时
         local h = getbinhandler(op1, op2, "__add")

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -