Lua脚本
Lua标准手册
VG3扩展功能手册
VG3 Lua脚本Demo
VG3 Lua手册索引
Lua脚本公共库
PublicDataFunc
PublicMathFunc
LuaModbus读写应用示例
通过TCP触发流程与写入全局变量
【技术文档】VG3 Lua API 技术文档
【技术文档】LuaGlobalVar — 全局变量操作
【技术文档】ZMotion - 运动控制模块
本站点使用 MrDoc 构建
-
+
Lua标准手册
> 此文件包含纯Lua语言的标准语法和库函数文档 ## 1. 基本类型 Lua 是一门动态类型语言。 这意味着变量没有类型;只有值才有类型。 语言中不设类型定义。 所有的值携带自己的类型。Lua 中所有的值都是 一等公民 。 这意味着所有的值均可保存在变量中、 当作参数传递给其它函数、以及作为返回值。Lua 中有八种基本类型: nil 、boolean 、number 、string 、function 、userdata 、 thread 和 table 。 nil 是值nil 的类型, 其主要特征就是和其它值区别开;通常用来表示一个有意义的值不存在时的状态。 boolean 是false 与true 两个值的类型。nil 和false 都会导致条件判断为假; 而其它任何值都表示为真。 number 代表了整数和实数(浮点数)。number 类型有两种内部表现方式,整数 和浮点数 。 对于何时使用哪种内部形式,Lua 有明确的规则, 但它也按需作自动转换。 因此,程序员多数情况下可以选择忽略整数与浮点数之间的差异或者假设完全控制每个数字的内部表现方式。number 类型底层使用 64 位整数和双精度(64 位)浮点数表示。 string 表示一个不可变的字节序列。 Lua 对 8 位是友好的: 字符串可以容纳任意 8 位值, 其中包含零 (\0) 。 Lua 的字符串与编码无关; 它不关心字符串中具体内容。 function 表示 Lua 或 C 编写的函数。 userdata 类型允许将 C 中的数据保存在 Lua 变量中。 用户数据类型的值是一个内存块, 有两种用户数据:完全用户数据 ,指一块由 Lua 管理的内存对应的对象;轻量用户数据 ,则指一个简单的 C 指针。你只能通过 C API 而无法在 Lua 代码中创建或者修改用户数据的值,因此在Lua 代码里不需要关心此类型。 thread 类型表示了一个独立的执行序列,被用于实现协程 。 Lua 的线程与操作系统的线程毫无关系,所有的协程都在同一个操作系统线程里运行,因此同一时间只能有一个协程在执行。 Lua 为所有的系统,包括那些不支持原生线程的系统,提供了协程支持。 table 是一个关联数组, 也就是说,这个数组不仅仅以数字做索引,除了nil 和 NaN (一个特殊的数字,它用于表示未定义或表示不了的运算结果,比如0/0)之外的所有 Lua 值 都可以做索引。 表可以是异构 的; 也就是说,表内可以包含任何类型的值(nil 除外)。 任何键的值若为nil 就不会被记入表结构内部。 换言之,对于表内不存在的键,都对应着值nil 。表是 Lua 中唯一的数据结构, 它可被用于表示普通数组、序列、符号表、集合、记录、图、树等等。 对于记录,Lua 使用域名作为索引。 语言提供了a.name 这样的语法糖来替代a["name"]这种写法以方便记录这种结构的使用。我们使用序列 这个术语来表示一个用 {1..n } 的正整数集做索引的表。 这里的非负整数n 被称为该序列的长度。和索引一样,表中每个域的值也可以是任何类型。 索引一张表的原则遵循语言中的直接比较规则。 当且仅当i 与j 直接比较相等时 (即不通过元方法的比较), 表达式a[i] 与a[j] 表示了表中相同的元素。 特别指出:一个可以完全表示为整数的浮点数和对应的整数相等 (例如:1.0 == 1)。 为了消除歧义,当一个可以完全表示为整数的浮点数做为键值时, 都会被转换为对应的整数储存。 例如,当你写a[2.0] = true 时, 实际被插入表中的键是整数2 。 (另一方面,2 与 "2" 是两个不同的 Lua 值, 故而它们可以是同一张表中的不同项。) 表、函数、线程、以及完全用户数据在 Lua 中被称为 对象: 变量并不真的持有它们的值,而仅保存了对这些对象的 引用。 赋值、参数传递、函数返回,都是针对引用而不是针对值的操作, 这些操作均不会做任何形式的隐式拷贝。库函数 type 用于以字符串形式返回给定值的类型。 ## 2. 语言定义 ### 2.1 词法约定 Lua 中的 名字 (也被称为 标识符 ) 可以是由非数字打头的任意字母下划线和数字构成的字符串。 标识符可用于对变量、表的域、以及标签命名。 下列 关键字 是保留的,不可用于名字: ``` and break do else elseif end false for function goto if in local nil not or repeat return then true until while ``` Lua 语言对大小写敏感: and 是一个保留字,但 And 与 AND 则是两个不同的有效名字。 作为一个约定,程序应避免创建以下划线加一个或多个大写字母构成的名字 (例如 _VERSION)。 下列字符串是内部操作符: ``` + - * / % ^ # & ~ | << >> // == ~= <= >= < > = ( ) { } [ ] :: ; : , . .. ... ``` 字面串 可以用单引号或双引号括起。 字面串内部可以包含下列 C 风格的转义串: '\a' (响铃), '\b' (退格), '\f' (换页), '\n' (换行), '\r' (回车), '\t' (横项制表), '\v' (纵向制表), '\\' (反斜杠), '\"' (双引号), 以及 '\'' (单引号)。 在反斜杠后跟一个真正的换行等价于在字符串中写一个换行符。 Lua 中的字符串可以保存任意 8 位值,其中包括用 '\0' 表示的 0 。 一般而言,你可以用字符的数字值来表示这个字符。 方式是用转义串 \xXX, 此处的 XX 必须是恰好两个字符的 16 进制数。 或者你也可以使用转义串 \ddd, 这里的 ddd 是一到三个十进制数字。 (注意,如果在转义符后接着恰巧是一个数字符号的话, 你就必须在这个转义形式中写满三个数字。) 对于用 UTF-8 编码的 Unicode 字符,你可以用 转义符 \u{XXX} 来表示 (这里必须有一对花括号), 此处的 XXX 是用 16 进制表示的字符编号。 字面串还可以用一种 长括号 括起来的方式定义。 我们把两个正的方括号间插入 n 个等号定义为 第 n 级开长括号 。 就是说,0 级开的长括号写作 [[ , 一级开长括号写作 [=[ , 如此等等。 闭长括号也作类似定义; 举个例子,4 级反的长括号写作 ]====] 。 一个 长字面串 可以由任何一级的开长括号开始,而由第一个碰到的同级的闭长括号结束。 这种方式描述的字符串可以包含任何东西,当然特定级别的反长括号除外。 整个词法分析过程将不受分行限制,不处理任何转义符,并且忽略掉任何不同级别的长括号。 其中碰到的任何形式的换行串(回车、换行、回车加换行、换行加回车),都会被转换为单个换行符。 字面串中的每个不被上述规则影响的字节都呈现为本身。 然而,Lua 是用文本模式打开源文件解析的, 一些系统的文件操作函数对某些控制字符的处理可能有问题。 因此,对于非文本数据,用引号括起来并显式按转义符规则来表述更安全。 为了方便起见, 当一个开长括号后紧接一个换行符时, 这个换行符不会放在字符串内。 举个例子,假设一个系统使用 ASCII 码 (此时 'a' 编码为 97 , 换行编码为 10 ,'1' 编码为 49 ), 下面五种方式描述了完全相同的字符串: ```lua a = 'alo\n123"' a = "alo\n123\"" a = '\97lo\10\04923"' a = [[alo 123"]] a = [==[ alo 123"]==] ``` 数字常量 可以由可选的小数部分和可选的十为底的指数部分构成, 指数部分用字符 'e' 或 'E' 来标记。 Lua 也接受以 0x 或 0X 开头的 16 进制常量。 16 进制常量也接受小数加指数部分的形式,指数部分是以二为底, 用字符 'p' 或 'P' 来标记。 数字常量中包含小数点或指数部分时,被认为是一个浮点数; 否则被认为是一个整数。 下面有一些合法的整数常量的例子: ``` 3 345 0xff 0xBEBADA ``` 以下为合法的浮点常量: ``` 3.0 3.1416 314.16e-2 0.31416E1 34e1 0x0.1E 0xA23p-4 0X1.921FB54442D18P+1 ``` 在字符串外的任何地方出现以双横线 (--) 开头的部分是 注释 。 如果 -- 后没有紧跟着一个开大括号, 该注释为 短注释 , 注释到当前行末截至。 否则,这是一段 长注释 , 注释区一直维持到对应的闭长括号。 长注释通常用于临时屏蔽掉一大段代码。 ### 2.2 变量 变量是储存值的地方。 Lua 中有三种变量: 全局变量、局部变量和表的域。 单个名字可以指代一个全局变量也可以指代一个局部变量 (或者是一个函数的形参,这是一种特殊形式的局部变量)。声明为 local的变量名,即为局部变量。局部变量可以在语句块中任何地方声明。 声明可以包含一个初始化赋值操作:local var = {},若没有初始化值,所有的变量都被初始化为 nil 。 所有没有显式声明为局部变量的变量名都被当做全局变量。 局部变量有其 作用范围 : 局部变量可以被定义在它作用范围中的函数自由使用。在变量的首次赋值之前,变量的值均为 nil 。 方括号被用来对表作索引:var["Name"]。var.Name 这种语法只是一个语法糖,用来表示 var["Name"]。 ### 2.3 语句 #### 2.3.1 语句块 语句块是一个语句序列,它们会按次序执行。Lua 支持 空语句 , 你可以用分号分割语句,也可以以分号开始一个语句块, 或是连着写两个分号: ```lua a = b + c ;a = b + c a = b + c; a = b + c;; ``` 这四种方式解析结果一致。 一个语句块可以使用 do...end被显式的定界为单条语句: ```lua do local b = c + d local a = b / e end ``` 显式的对一个块定界通常用来控制内部变量声明的作用域。 这里的变量a和b只在此语句块中有效。 #### 2.3.2 代码块 Lua 的一个编译单元被称为一个 代码块 。 从句法构成上讲,一个代码块就是一个语句块。 Lua 把一个代码块当作一个拥有不定参数的匿名函数来处理。 正是这样,代码块内可以定义局部变量,它可以接收参数,返回若干值。 此外,这个匿名函数在编译时还为它的作用域绑定了一个外部局部变量 _ENV。 该函数总是把 _ENV 作为它唯一的一个上值,即使这个函数不使用这个变量,它也存在。 代码块可以被保存在文件中,也可以作为宿主程序内部的一个字符串。 要执行一个代码块, 首先要让 Lua 加载 它, 将代码块中的代码预编译成虚拟机中的指令, 而后,Lua 用虚拟机解释器来运行编译后的代码。 代码块可以被预编译为二进制形式;用源码表示的程序和编译后的形式可自由替换; Lua 会自动检测文件格式做相应的处理。 #### 2.3.3 赋值 Lua 允许多重赋值。因此,赋值的语法定义是等号左边放一个变量列表, 而等号右边放一个表达式列表。 两边的列表中的元素都用逗号间开: ```lua var1, var2, var3, ... = exp1, exp2, exp3, ... ``` 在作赋值操作之前, 那值列表会被 调整 为左边变量列表的个数。 如果值比需要的更多的话,多余的值就被扔掉。 如果值的数量不够需求, 将会按所需扩展若干个 nil 。 如果表达式列表以一个函数调用结束, 这个函数所返回的所有值都会在调整操作之前被置入值列表中 (除非这个函数调用被用括号括了起来)。 赋值语句首先让所有的表达式完成运算, 之后再做赋值操作。 因此,下面这段代码 ```lua i = 3 i, a[i] = i+1, 20 ``` 会把 a[3] 设置为 20,而不会影响到 a[4]。这是因为 a[i] 中的 i 在被赋值为 4 之前就被计算出来了(当时是 3 )。 简单说 ,这样一行 ```lua x, y = y, x ``` 会交换 x 和 y 的值, 及 ```lua x, y, z = y, z, x ``` 会轮换 x,y,z 的值。 #### 2.3.4 控制结构 if, while, repeat 这些控制结构符合通常的意义,而且也有类似的语法: ```lua while exp do block end repeat block until exp if exp then block {elseif exp then block} [else block] end ``` 控制结构中的条件表达式可以返回任何值。 false 与 nil 两者都被认为是假。 所有不同于 nil 与 false 的其它值都被认为是真 (特别需要注意的是,数字 0 和空字符串也被认为是真)。在 repeat –until 循环中, 内部语句块的结束点不是在 until 这个关键字处, 它还包括了其后的条件表达式。 因此,条件表达式中可以使用循环内部语句块中的定义的局部变量。 goto 语句将程序的控制点转移到一个标签处。 由于句法上的原因, Lua 里的标签也被认为是语句: ```lua ::Name:: --定义 goto Name --跳转 ``` 除了在内嵌函数中,以及在内嵌语句块中定义了同名标签,的情况外, 标签对于它定义所在的整个语句块可见。只要 goto 没有进入一个新的局部变量的作用域,它可以跳转到任意可见标签处。break 被用来结束 while 、 repeat 、或 for 循环, 它将跳到循环外接着之后的语句运行。 return 被用于从函数或是代码块(其实它就是一个函数) 中返回值。 函数可以返回不止一个值,所以 return 的语法为 ```lua return exp1, exp2, ... ``` return 只能被写在一个语句块的最后一句。 如果你真的需要从语句块的中间 return , 你可以使用显式的定义一个内部语句块, 一般写作 do return end。 可以这样写是因为现在 return 成了(内部)语句块的最后一句了。 #### 2.3.5 for语句 for 有两种形式:一种是数字形式,另一种是通用形式。 数字形式的 for 循环,通过一个数学运算不断地运行内部的代码块。 下面是它的语法: ```lua for var = exp1, exp2 [, exp3] do block end ``` block 将把 name 作循环变量。 从 exp1 开始起,直到 exp2 的值为止, 其步长为 exp3 。 注意下面这几点: - 所有三个控制表达式都只被运算一次, 表达式的计算在循环开始之前。 这些表达式的结果必须是数字。 - 如果exp3(步长)没有给出,会把步长设为 1 。 - 你可以用break 和goto 来退出for 循环。 - 循环变量 var 是一个循环内部的局部变量; 如果你需要在循环结束后使用这个值, 在退出循环前把它赋给另一个变量。 通用形式的for 通过一个叫作迭代器 的函数工作。 每次迭代,迭代器函数都会被调用以产生一个新的值, 当这个值为nil 时,循环停止。 通用形式的for 循环的语法如下: ```lua for var1, var2, ... in explist do block end ``` 注意以下几点: - explist 只会被计算一次。 它返回三个值, 一个迭代器 函数, 一个状态 , 一个迭代器的初始值 。 - var1, var2, ...是迭代器函数的返回值 - 你可以使用break 来跳出for 循环。 - 环变量var1, var2, ... 对于循环来说是一个局部变量; 你不可以在for 循环结束后继续使用。 如果你需要保留这些值,那么就在循环跳出或结束前赋值到别的变量里去。 一般用法是遍历table: ```lua local tab = {} for k, v in pairs(tab) do end ``` ### 2.4 表达式 #### 2.4.1 数学运算操作符 Lua 支持下列数学运算操作符: - +: 加法 - -: 减法 - *: 乘法 - /: 浮点除法 - //: 向下取整除法 - %: 取模 - ^: 乘方 - -: 取负 除了乘方和浮点除法运算, 数学运算按如下方式工作: 如果两个操作数都是整数, 该操作以整数方式操作且结果也将是一个整数。 否则,当两个操作数都是数字或可以被转换为数字的字符串时, 操作数会被转换成两个浮点数, 操作按通常的浮点规则(一般遵循 IEEE 754 标准) 来进行,结果也是一个浮点数。 乘方和浮点除法 (/) 总是把操作数转换成浮点数进行,其结果总是浮点数。 乘方使用 ISO C 函数 pow, 因此它也可以接受非整数的指数。 向下取整的除法 (//) 指做一次除法,并将商圆整到靠近负无穷的一侧, 即对操作数做除法后取 floor 。 取模被定义成除法的余数,其商被圆整到靠近负无穷的一侧(向下取整的除法)。 对于整数数学运算的溢出问题, 这些操作采取的策略是按通常遵循的以 2 为补码的数学运算的 环绕 规则。 (换句话说,它们返回其运算的数学结果对 264 取模后的数字。) #### 2.4.2 位操作符 Lua 支持下列位操作符: - &: 按位与 - |: 按位或 - ~: 按位异或 - >>: 右移 - <<: 左移 - ~: 按位非 所有的位操作都将操作数先转换为整数, 然后按位操作,其结果是一个整数。 对于右移和左移,均用零来填补空位。 移动的位数若为负,则向反方向位移; 若移动的位数的绝对值大于等于 整数本身的位数,其结果为零 (所有位都被移出)。 #### 2.4.3 强制转换 Lua 对一些类型和值的内部表示会在运行时做一些数学转换。 位操作总是将浮点操作数转换成整数。 乘方和浮点除法总是将整数转换为浮点数。 其它数学操作若针对混合操作数 (整数和浮点数)将把整数转换为浮点数; 这一点被称为 通常规则 。 C API 同样会按需把整数转换为浮点数以及 把浮点数转换为整数。 此外,字符串连接操作除了字符串,也可以接受数字作为参数。 当操作需要数字时,Lua 还会把字符串转换为数字。 当把一个整数转换为浮点数时, 若整数值恰好可以表示为一个浮点数,那就取那个浮点数。 否则,转换会取最接近的较大值或较小值来表示这个数。 这种转换是不会失败的。 将浮点数转为整数的过程会检查 浮点数能否被准确的表达为一个整数 (即,浮点数是一个整数值且在整数可以表达的区间)。 如果可以,结果就是那个数,否则转换失败。 从字符串到数字的转换过程遵循以下流程: 首先,遵循按 Lua 词法分析器的规则分析语法来转换为对应的 整数或浮点数。 (字符串可以有前置或后置的空格以及一个符号。) 然后,结果数字再按前述规则转换为所需要的类型(浮点或整数)。 从数字转换为字符串使用非指定的人可读的格式。 若想完全控制数字到字符串的转换过程, 可以使用字符串库中的 string.format 函数 #### 2.4.4 比较操作符 Lua 支持下列比较操作符: - ==: 等于 - ~=: 不等于 - <: 小于 - >: 大于 - <=: 小于等于 - >=: 大于等于 这些操作的结果不是 false 就是 true 。 等于操作 (==)先比较操作数的类型。 如果类型不同,结果就是 false 。 否则,继续比较值。 字符串按一般的方式比较。 数字遵循二元操作的规则: 如果两个操作数都是整数, 它们按整数比较; 否则,它们先转换为浮点数,然后再做比较。 表,用户数据,以及线程都按引用比较: 只有两者引用同一个对象时才认为它们相等。 每次你创建一个新对象(一张表,一个用户数据,或一个线程), 新对象都一定和已有且存在的对象不同。 相同引用的闭包一定相等。 有任何可察觉的差异(不同的行为,不同的定义)一定不等。 等于操作不会将字符串转换为数字,反之亦然。 即,"0"==0 结果为 false , 且 t[0] 与 t["0"] 指代着表中的不同项。 ~= 操作完全等价于 == 操作的反值。 大小比较操作以以下方式进行。 如果参数都是数字, 它们按二元操作的常规进行。 否则,如果两个参数都是字符串, 它们的值按当前的区域设置来比较。a > b 的比较被转译为 b < a, a >= b 被转译为 b <= a。 #### 2.4.5 逻辑操作符 Lua 中的逻辑操作符有 and , or ,以及 not 。 和控制结构(参见 2.3.4)一样, 所有的逻辑操作符把 false 和 nil 都作为假, 而其它的一切都当作真。 取反操作 not 总是返回 false 或 true 中的一个。 与操作符 and 在第一个参数为 false 或 nil 时 返回这第一个参数; 否则,and 返回第二个参数。 或操作符 or 在第一个参数不为 nil 也不为 false 时, 返回这第一个参数,否则返回第二个参数。 and 和 or 都遵循短路规则; 也就是说,第二个操作数只在需要的时候去求值。 这里有一些例子: ```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 ``` #### 2.4.6 字符串连接 Lua 中字符串的连接操作符写作两个点(..)。 如果两个操作数都是字符串或都是数字, 连接操作将以 2.4.3 中提到的规则把其转换为字符串。 否则,会调用元方法 __concat。 #### 2.4.7 取长度操作符 取长度操作符写作一元前置符 #。 字符串的长度是它的字节数(就是以一个字符一个字节计算的字符串长度)。 程序可以通过 __len 元方法来修改对字符串类型外的任何值的取长度操作行为。 如果 __len 元方法没有给出, 表的长度只在表是一个 序列 时有定义。 序列指表的正数键集等于 {1..n} , 其中 n 是一个非负整数。 在这种情况下,n 是表的长度。 注意这样的表 ```lua {10, 20, nil, 40} ``` 不是一个序列,因为它有键 4 却没有键 3。 (因此,该表的正整数键集不等于 {1..n} 集合,故而就不存在 n 。) ,此时使用 # 操作符得到的值为2。注意,一张表是否是一个序列和它的非数字键无关。 #### 2.4.8 操作符优先级 Lua 中操作符的优先级写在下表中,从低到高优先级排序: ``` or and < > <= >= ~= == | ~ & << >> .. + - * / // % 一元操作符 (not # - ~) ^ ``` 通常, 你可以用括号来改变运算次序。 连接操作符 (..) 和乘方操作 (^) 是从右至左的。 其它所有的操作都是从左至右。 #### 2.4.9 表结构 表构造子是一个构造表的表达式。 每次构造子被执行,都会构造出一张新的表。 构造子可以被用来构造一张空表, 也可以用来构造一张表并初始化其中的一些域。 一般的构造子的语法如下: ```lua {var1, var2, ....} {[key1] = var1, [key2] = var2, ...} {name1 = var1, name2 = var2, ...} ``` 每个形如 [key1] = var1 的域向表中增加新的一项, 其键为 key1 而值为 var1 形如 name1 = var1 的域等价于 ["name1"] = var1 最后,形如 var1 的域等价于 [i] = var1 , 这里的 i 是一个从 1 开始不断增长的数字。 这这个格式中的其它域不会破坏其记数。 举个例子: ```lua a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 } ``` 等价于: ```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 ``` 如果表单中最后一个域的形式是 exp , 而且其表达式是一个函数调用或者是一个可变参数, 那么这个表达式所有的返回值将依次进入列表,如果不是最后一个域,那么只有第一个值会进入列表。 #### 2.4.10 函数调用 Lua 中的函数调用的语法如下: ```lua local result = func(arg1, arg2, ...) ``` 函数调用时, 第一步,func 和 arg1, arg2,... 先被求值。 如果 func 的值的类型是 function , 那么这个函数就被用给出的参数调用。 否则 func 的元方法 "__call" 就被调用, 第一个参数是 func 的值, 接下来的是原来的调用参数。 这样的形式 ```lua local result = t:name(arg1, arg2, ...) ``` 可以用来调用 "方法"。 这是 Lua 支持的一种语法糖。 像 t:name(arg1, arg2, ...) 这个样子, 被解释成 t.name(t, arg1, arg2, ...), 这里的 v 只会被求值一次。 所有参数的表达式求值都在函数调用之前。 这样的调用形式 f{fields} 是一种语法糖用于表示 f({fields}); 这里指参数列表是一个新创建出来的列表。 而这样的形式 f'string' (或是 f"string" 亦或是 f[[string]]) 也是一种语法糖,用于表示 f('string'); 此时的参数列表是一个单独的字符串。 return func(arg1, arg2, ...) 这样的调用形式将触发一次 尾调用 。 Lua 实现了 完全尾调用 (或称为 完全尾递归 ): 在尾调用中, 被调用的函数重用调用它的函数的堆栈项。 因此,对于程序执行的嵌套尾调用的层数是没有限制的。 然而,尾调用将删除调用它的函数的任何调试信息。 注意,尾调用只发生在特定的语法下, 仅当 return 只有单一函数调用作为参数时才发生尾调用; 这种语法使得调用函数的所有结果可以完整地返回。 因此,下面这些例子都不是尾调用: ```lua return (f(x)) -- 返回值被调整为一个 return 2 * f(x) return x, f(x) -- 追加若干返回值 f(x); return -- 返回值全部被舍弃 return x or f(x) -- 返回值被调整为一个 ``` #### 2.4.11 函数定义 函数定义的语法如下: ```lua function name(args...) end ``` 另外定义了一些语法糖简化函数定义的写法: ```lua function t.name(args...) end function t:name(args...) end local function name(args) end ``` 该语句 ```lua function f () end ``` 被转译成 ```lua f = function () end ``` 该语句 ```lua function t.a.b.c.f () end ``` 被转译成 ```lua t.a.b.c.f = function () end ``` 该语句 ```lua local function f () end ``` 被转译成 ```lua local f; f = function () end ``` 而不是 ```lua local f = function () end ``` (这个差别只在函数体内需要引用 f 时才有。) 一个函数定义是一个可执行的表达式, 执行结果是一个类型为 function 的值。 当 Lua 预编译一个代码块时, 代码块作为一个函数,整个函数体也就被预编译了。 那么,无论何时 Lua 执行了函数定义, 这个函数本身就进行了 实例化 。 这个函数的实例(或者说是 闭包 )是表达式的最终值。 当一个函数被调用, 如果函数并非一个 可变参数函数 , 即在形参列表的末尾注明三个点 (...), 那么实参列表就会被调整到形参列表的长度。 变长参数函数不会调整实参列表; 取而代之的是,它将把所有额外的参数放在一起通过变长参数表达式传递给函数, 其写法依旧是三个点。 这个表达式的值是一串实参值的列表, 看起来就跟一个可以返回多个结果的函数一样。 如果一个变长参数表达式放在另一个表达式中使用, 或是放在另一串表达式的中间, 那么它的返回值就会被调整为单个值。 若这个表达式放在了一系列表达式的最后一个, 就不会做调整了 (除非这最后一个参数被括号给括了起来)。 我们先做如下定义,然后再来看一个例子: ```lua 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 来返回,如果执行到函数末尾依旧没有遇到任何 return 语句, 函数就不会返回任何结果。 关于函数可返回值的数量限制和系统有关。 这个限制一定大于 1000 。 冒号 语法可以用来定义 方法 , 就是说,函数可以有一个隐式的形参 self。 因此,如下语句 ```lua function t.a.b.c:f (args) end ``` 是这样一种写法的语法糖 ```lua t.a.b.c.f = function (self, args) end ``` ### 2.5 可见性规则 Lua 语言有词法作用范围。 变量的作用范围开始于声明它们之后的第一个语句段, 结束于包含这个声明的最内层语句块的最后一个非空语句。 看下面这些例子: ```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 指向的是外面一层的变量。 因为有这样一个词法作用范围的规则, 局部变量可以被在它的作用范围内定义的函数自由使用。 当一个局部变量被内层的函数中使用的时候, 它被内层函数称作 上值 ,或是 外部局部变量 。 注意,每次执行到一个 local 语句都会定义出一个新的局部变量。 看看这样一个例子: ```lua a = {} local x = 20 for i=1,10 do local y = 0 a[i] = function () y=y+1; return x+y end end ``` 这个循环创建了十个闭包(这指十个匿名函数的实例)。 这些闭包中的每一个都使用了不同的 y 变量, 而它们又共享了同一份 x。 ## 3. 标准库 ### 3.1 基础函数 #### assert(v [, message]) 如果其参数 v 的值为假(nil 或 false ), 它就调用 error; 否则,返回所有的参数。 在错误情况时, message 指那个错误对象; 如果不提供这个参数,参数默认为 "assertion failed!" 。 #### error(message [, level]) 中止上一次保护函数调用, 将错误对象 message 返回。 函数 error 永远不会返回。当 message 是一个字符串时,通常 error 会把一些有关出错位置的信息附加在消息的前头。 level 参数指明了怎样获得出错位置。 对于 level 1 (默认值),出错位置指 error 函数调用的位置。 Level 2 将出错位置指向调用 error的函数的函数;以此类推。 传入 level 0 可以避免在消息前添加出错位置信息。 #### ipairs(t) 返回三个值(迭代函数、表 t 以及 0 ), 如此,以下代码 ```lua for i,v in ipairs(t) do end ``` 将迭代键值对(1,t[1]) ,(2,t[2]), … ,直到第一个空值。 #### next(table [, index]) 运行程序来遍历表中的所有域。 第一个参数是要遍历的表,第二个参数是表中的某个键。 next 返回该键的下一个键及其关联的值。 如果用 nil 作为第二个参数调用 next 将返回初始键及其关联值。 当以最后一个键去调用,或是以 nil 调用一张空表时, next 返回 nil 。 如果不提供第二个参数,将认为它就是 nil 。 特别指出,你可以用 next(t) 来判断一张表是否是空的。 索引在遍历过程中的次序无定义, 即使是数字索引也是这样 。 (如果想按数字次序遍历表,可以使用数字形式的 for 。) 当在遍历过程中你给表中并不存在的域赋值, next 的行为是未定义的。 然而你可以去修改那些已存在的域。 特别指出,你可以清除一些已存在的域。 #### pairs(t) 如果 t 有元方法 __pairs, 以 t 为参数调用它,并返回其返回的前三个值。 否则,返回三个值:next 函数, 表 t,以及 nil 。 因此以下代码 ```lua for k,v in pairs(t) do end ``` 能迭代表 t 中的所有键值对。 参见函数 next 中关于迭代过程中修改表的风险。 #### tonumber(e [, base]) 如果调用的时候没有 base, tonumber 尝试把参数转换为一个数字。 如果参数已经是一个数字,或是一个可以转换为数字的字符串, tonumber 就返回这个数字; 否则返回 nil 。 字符串的转换结果可能是整数也可能是浮点数, 这取决于 Lua 的转换文法。 (字符串可以有前置和后置的空格,可以带符号。) 当传入 base 调用它时, e 必须是一个以该进制表示的整数字符串。 进制可以是 2 到 36 (包含 2 和 36)之间的任何整数。 大于 10 进制时,字母 A (大小写均可)表示 10 , B 表示 11,依次到 Z 表示 35 。 如果字符串 e 不是该进制下的合法数字, 函数返回 nil 。 #### tostring(v) 可以接收任何类型,它将其转换为人可阅读的字符串形式。 浮点数总被转换为浮点数的表现形式(小数点形式或是指数形式)。 (如果想完全控制数字如何被转换,可以使用 tring.format。) 如果 v 有 __tostring 元方法, tostring 会以 v 为参数调用它。 并用它的结果作为返回值。 #### type(v) 将参数的类型编码为一个字符串返回。 函数可能的返回值有 "nil" (一个字符串,而不是 nil 值), "number", "string", "boolean", "table", "function", "thread", "userdata"。 #### require(modname) 加载一个模块。 这个函数首先检测是否被加载过。 如果被加载过,require 返回 之前返回的值。 否则,它试着寻找此模块 。 require 遵循一定序列的指引来查找模块。 下列说明查找规则。 首先 require 使用 Lua 加载器去查找当前路径及环境路径里modname.lua文件。 如果查找失败,接着使用 C 加载器去查找当前路径及环境路径里modname.dll(linux下是modname.so)。 如果找到了.lua文件,则加载并执行此文件,执行的返回值即为require的返回值。 如果找到了.dll文件,则加载并调用动态库的入口函数,入口函数的返回值即为require的返回值。 具体规则后面会详细说明。 如果在加载或运行模块时有错误, 或是无法为模块找到加载器, require 都会抛出错误。 ### 3.2 字符串库 这个库提供了字符串处理的通用函数。 例如字符串查找、子串、模式匹配等。 当在 Lua 中对字符串做索引时,第一个字符从 1 开始计算(而不是 C 里的 0 )。 索引可以是负数,它指从字符串末尾反向解析。 即,最后一个字符在 -1 位置处,等等。 字符串库中的所有函数都在表 string 中。 它还将其设置为字符串元表的 __index 域。 因此,你可以以面向对象的形式使用字符串函数。 例如,string.byte(s,i) 可以写成 s:byte(i)。 字符串库假定采用单字节字符编码。 #### string.byte(s [, i [, j]]) 返回字符 s[i], s[i+1], … ,s[j] 的内部数字编码。 i 的默认值是 1 ; j 的默认值是 i。 这些索引以函数 string.sub 的规则修正。 #### string.char(...) 接收零或更多的整数。 返回和参数数量相同长度的字符串。 其中每个字符的内部编码值等于对应的参数值。 #### string.find(s, pattern [, init [, plain]]) 查找第一个字符串 s 中匹配到的 pattern。 如果找到一个匹配,find 会返回 s 中关于它起始及终点位置的索引; 否则,返回 nil 。 第三个可选数字参数 init 指明从哪里开始搜索; 默认值为 1 ,同时可以是负值。 第四个可选参数 plain 为 true 时, 关闭模式匹配机制。 此时函数仅做直接的 "查找子串"的操作, 而 pattern 中没有字符被看作魔法字符。 注意,如果给定了 plain ,就必须写上 init 。 如果在模式中定义了捕获,捕获到的若干值也会在两个索引之后返回。具体模式匹配规则后而详述。 #### string.format(formatstring, ...) 返回不定数量参数的格式化版本, 格式化串为第一个参数(必须是一个字符串)。 格式化字符串遵循 ISO C 函数 sprintf 的规则。 不同点在于选项 *, h, L, l, n, p 不支持, 另外还增加了一个选项 q。 q 选项将一个字符串格式化为两个双引号括起,对内部字符做恰当的转义处理的字符串。 该字符串可以安全的被 Lua 解释器读回来。 例如,调用 ```lua string.format('%q', 'a string with "quotes" and \n new line') ``` 会产生字符串: ``` "a string with \"quotes\" and \ new line" ``` 选项 A and a (如果有的话), E, e, f, G, g 都期待一个对应的数字参数。 选项 c, d, i, o, u, X, x 则期待一个整数。 选项 q 期待一个字符串; 选项 s 期待一个没有内嵌零的字符串。 如果选项 s 对应的参数不是字符串,它会用和 tostring 一致的规则转换成字符串。 #### string.gmatch(s, pattern) 返回一个迭代器函数。 每次调用这个函数都会继续以 pattern 对 s 做匹配,并返回所有捕获到的值。 如果 pattern 中没有指定捕获,则每次捕获整个 pattern。 下面这个例子会循环迭代字符串 s 中所有的单词, 并逐行打印: ```lua s = "hello world from Lua" for w in string.gmatch(s, "%a+") do print(w) end ``` 下一个例子从指定的字符串中收集所有的键值对 key=value 置入一张表: ```lua t = {} s = "from=world, to=Lua" for k, v in string.gmatch(s, "(%w+)=(%w+)") do t[k] = v end ``` 模式匹配规则后面会详述。 #### string.gsub(s, pattern, repl [, n]) 将字符串 s 中,所有的(或是在 n 给出时的前 n 个) pattern 都替换成 repl ,并返回其副本。 repl 可以是字符串、表、或函数。 gsub 还会在第二个返回值返回一共发生了多少次匹配。 如果 repl 是一个字符串,那么把这个字符串作为替换品。 字符 % 是一个转义符: repl 中的所有形式为 %d 的串表示 第 d 个捕获到的子串,d 可以是 1 到 9 。 串 %0 表示整个匹配。 串 %% 表示单个 %。 如果 repl 是张表,每次匹配时都会用第一个捕获物作为键去查这张表。 如果 repl 是个函数,则在每次匹配发生时都会调用这个函数。 所有捕获到的子串依次作为参数传入。 任何情况下,模板中没有设定捕获都看成是捕获整个模板。 如果表的查询结果或函数的返回结果是一个字符串或是个数字, 都将其作为替换用串; 而在返回 false 或 nil 时不作替换 (即保留匹配前的原始串)。 这里有一些用例: ```lua x = string.gsub("hello world", "(%w+)", "%1 %1") --> x="hello hello world world" x = string.gsub("hello world", "%w+", "%0 %0", 1) --> x="hello hello world" x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1") --> x="world hello Lua from" x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv) --> x="home = /home/roberto, user = roberto" x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s) return load(s)() end) --> x="4+5 = 9" local t = {name="lua", version="5.4"} x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t) --> x="lua-5.4.tar.gz" ``` 具体模式匹配规则后面会详述。 #### string.len(s) 接收一个字符串,返回其长度。 空串 "" 的长度为 0 。 内嵌零也统计在内,因此 "a\000bc\000" 的长度为 5 。 #### string.lower(s) 接收一个字符串,将其中的大写字符都转为小写后返回其副本。 其它的字符串不会更改。 对大写字符的定义取决于当前的区域设置。 #### string.match(s, pattern [, init]) 在字符串 s 中找到第一个能用 pattern 匹配到的部分。 如果能找到,返回其中的捕获物; 否则返回 nil 。 如果 pattern 中未指定捕获, 返回整个 pattern 捕获到的串。 第三个可选数字参数 init 指明从哪里开始搜索; 它默认为 1 且可以是负数。 具体模式匹配规则后面详述。 #### string.rep(s, n [, sep]) 返回 n 个字符串 s 以字符串 sep 为分割符连在一起的字符串。 默认的 sep 值为空字符串(即没有分割符)。 如果 n 不是正数则返回空串。 #### string.reverse(s) 返回字符串 s 的翻转串。 #### string.sub(s, i [, j]) 返回 s 的子串, 该子串从 i 开始到 j 为止; i 和 j 都可以为负数。 如果不给出 j ,就当它是 -1 (和字符串长度相同)。 特别是, 调用 string.sub(s,1,j) 可以返回 s 的长度为 j 的前缀串, 而 string.sub(s, -i) 返回长度为 i 的后缀串。 如果在对负数索引转义后 i 小于 1 的话,就修正回 1 。 如果 j 比字符串的长度还大,就修正为字符串长度。 如果在修正之后,i 大于 j, 函数返回空串。 #### string.upper(s) 接收一个字符串,将其中的小写字符都转为大写后返回其副本。 其它的字符串不会更改。 对小写字符的定义取决于当前的区域设置。 ### 3.3 模式匹配 Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。 这一节表述了这些字符串的语法及含义(即它能匹配到什么)。 #### 3.3.1 字符类 字符类 用于表示一个字符集合。 下列组合可用于字符类: - x : (这里x 不能是魔法字符^$()%.[]*+-? 中的一员) 表示字符x 自身。 - .: (一个点)可表示任何字符。 - %a: 表示任何字母。 - %c: 表示任何控制字符。 - %d: 表示任何数字。 - %g: 表示任何除空白符外的可打印字符。 - %l: 表示所有小写字母。 - %p: 表示所有标点符号。 - %s: 表示所有空白字符。 - %u: 表示所有大写字母。 - %w: 表示所有字母及数字。 - %x: 表示所有 16 进制数字符号。 - %x: (这里的x 是任意非字母或数字的字符) 表示字符x 。 这是对魔法字符转义的标准方法。 所有非字母或数字的字符 (包括所有标点,也包括非魔法字符) 都可以用前置一个% 放在模式串中表示自身。 - [set]: 表示set 中所有字符的联合。 可以以- 连接,升序书写范围两端的字符来表示一个范围的字符集。 上面提到的%x 形式也可以在set 中使用 表示其中的一个元素。 其它出现在set 中的字符则代表它们自己。 例如,[%w_] (或[_%w]) 表示所有的字母数字加下划线(下划线加字母),[0-7] 表示 8 进制数字,[0-7%l%-] 表示 8 进制数字加小写字母与- 字符。 - [^set]: 表示set 的补集, 其中set 如上面的解释。 所有单个字母表示的类别(%a,%c,等), 若将其字母改为大写,均表示对应的补集。 例如,%S 表示所有非空格的字符。 如何定义字母、空格、或是其他字符组取决于当前的区域设置。 特别注意:[a-z] 未必等价于 %l 。 #### 3.3.2 模式条目 模式条目 可以是 - 单个字符类匹配该类别中任意单个字符; - 单个字符类跟一个*, 将匹配零或多个该类的字符。 这个条目总是匹配尽可能长的串; - 单个字符类跟一个+, 将匹配一或更多个该类的字符。 这个条目总是匹配尽可能长的串; - 单个字符类跟一个-, 将匹配零或更多个该类的字符。 和* 不同, 这个条目总是匹配尽可能短的串; - 单个字符类跟一个?, 将匹配零或一个该类的字符。 只要有可能,它会匹配一个; - %n, 这里的n 可以从 1 到 9; 这个条目匹配一个等于n 号捕获物(后面有描述)的子串。 - %bxy, 这里的x 和y 是两个明确的字符; 这个条目匹配以x 开始y 结束, 且其中x 和y 保持平衡 的字符串。 意思是,如果从左到右读这个字符串,对每次读到一个x 就+1 ,读到一个y 就-1, 最终结束处的那个y 是第一个记数到 0 的y 。 举个例子,条目%b() 可以匹配到括号平衡的表达式。 - %f[set], 指边境模式 ; 这个条目会匹配到一个位于set 内某个字符之前的一个空串, 且这个位置的前一个字符不属于set 。 集合set 的含义如前面所述。 匹配出的那个空串之开始和结束点的计算就看成该处有个字符\0 一样。 #### 3.3.3 模式 模式 指一个模式条目的序列。 在模式最前面加上符号 ^ 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 $ 将使匹配过程锚定到字符串的结尾。 如果 ^ 和 $ 出现在其它位置,它们均没有特殊含义,只表示自身。 #### 3.3.4 捕获 模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物 。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 "(a*(.)%w(%s*))" , 字符串中匹配到 "a*(.)%w(%s*)" 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 "." 匹配到的字符是 2 号捕获物, 匹配到 "%s*" 的那部分是 3 号。 作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 "()aa()" 作用到字符串 "flaaap" 上,将产生两个捕获物: 3 和 5 。 ### 3.4 UTF-8库 这个库提供了对 UTF-8 编码的基础支持。 所有的函数都放在表 utf8 中。 此库不提供除编码处理之外的任何 Unicode 支持。 所有需要了解字符含义的操作,比如字符分类,都不在此范畴。 除非另有说明, 当一个函数需要一个字节位置的参数时, 都假定这个位置要么从字节序列的开始计算, 要么从字符串长度加一的位置算。 和字符串库一样,负的索引从字符串末尾计起。 #### utf8.char(···) 接收零或多个整数, 将每个整数转换成对应的 UTF-8 字节序列,并返回这些序列连接到一起的字符串。 #### utf8.charpattern 用于精确匹配到一个 UTF-8 字节序列的模式(是一个字符串,并非函数)"[\0-\x7F\xC2-\xF4][\x80-\xBF]*"。 它假定处理的对象是一个合法的 UTF-8 字符串。 #### utf8.codes(s) 返回一系列的值,可以让 ```lua for p, c in utf8.codes(s) do end ``` 迭代出字符串 s 中所有的字符。 这里的 p 是位置(按字节数)而 c 是每个字符的编号。 如果处理到一个不合法的字节序列,将抛出一个错误。 #### utf8.codepoint(s [, i [, j]]) 以整数形式返回 s 中 从位置 i 到 j 间(包括两端) 所有字符的编号。 默认的 i 为 1 ,默认的 j 为 i。 如果碰上不合法的字节序列,抛出一个错误。 #### utf8.len(s [, i [, j]]) 返回字符串 s 中 从位置 i 到 j 间 (包括两端) UTF-8 字符的个数。 默认的 i 为 1 ,默认的 j 为 -1 。 如果它找到任何不合法的字节序列, 返回假值加上第一个不合法字节的位置。 #### utf8.offset(s, n [, i]) 返回编码在 s 中的第 n 个字符的开始位置(按字节数) (从位置 i 处开始统计)。 负 n 则取在位置 i 前的字符。 当 n 是非负数时,默认的 i 是 1, 否则默认为 #s + 1。 因此,utf8.offset(s, -n) 取字符串的倒数第 n 个字符的位置。 如果指定的字符不在其中或在结束点之后,函数返回 nil 。 作为特例,当 n 等于 0 时, 此函数返回含有 s 第 i 字节的那个字符的开始位置。 这个函数假定 s 是一个合法的 UTF-8 字符串。 ### 3.5 列表处理 这个库提供了表处理的通用函数。 所有函数都放在表 table 中。 记住,无论何时,若一个操作需要取表的长度, 这张表必须是一个真序列,或是拥有 __len 元方法。 所有的函数都忽略传入参数的那张表中的非数字键。 #### table.concat(list [, sep [, i [, j]]]) 提供一个列表,其所有元素都是字符串或数字,返回字符串 list[i]..sep..list[i+1]···sep..list[j]。 sep 的默认值是空串, i 的默认值是 1 , j 的默认值是 #list 。 如果 i 比 j 大,返回空串。 #### table.insert(list, [pos, ] value) 在 list 的位置 pos 处插入元素 value , 并后移元素 list[pos], list[pos+1], ···, list[#list] 。 pos 的默认值为 #list+1 , 因此调用 table.insert(t,x) 会将 x 插在列表 t 的末尾。 #### table.move(t1, p1, p2, p3 [, t2]) 将元素从表 t1 移到表 t2。 这个函数做了次等价于后面这个多重赋值的等价操作: t2[p3], ··· = t1[p1], ··· ,t1[p2]。 t2 的默认值为 t1。 目标区间可以和源区间重叠。 索引 p1 必须是正数。 #### table.pack(···) 返回用所有参数以键 1,2, 等填充的新表, 并将 n 这个域设为参数的总数。 注意这张返回的表不一定是一个序列。 #### table.remove(list [, pos]) 移除 list 中 pos 位置上的元素,并返回这个被移除的值。 当 pos 是在 1 到 #list 之间的整数时, 它向前移动元素 list[pos+1], list[pos+2], ···, list[#list] 并删除元素 list[#list]; 索引 pos 可以是 #list + 1 ,或在 #list 为 0 时可以是 0 ; 在这些情况下,函数删除元素 list[pos]。 pos 默认为 #list, 因此调用 table.remove(list) 将移除表 list 的最后一个元素。 #### table.sort(list [, comp]) 在表内从 list[1] 到 list[#list] 原地 对其间元素按指定次序排序。 如果提供了 comp , 它必须是一个可以接收两个列表内元素为参数的函数。 当第一个元素需要排在第二个元素之前时,返回真 (因此 not comp(list[i+1],list[i]) 在排序结束后将为真)。 如果没有提供 comp, 将使用标准 Lua 操作 < 作为替代品。 排序算法并不稳定; 即当两个元素次序相等时,它们在排序后的相对位置可能会改变。 #### table.unpack(list [, i [, j]]) 返回列表中的元素。 这个函数等价于 ```lua return list[i], list[i+1], ···, list[j] ``` i 默认为 1 ,j 默认为 #list。 ### 3.6 数学库 这个库提供了基本的数学函数。 所以函数都放在表 math 中。 注解有 integer/float 的函数会对整数参数返回整数结果, 对浮点(或混合)参数返回浮点结果。 圆整函数(math.ceil, math.floor, math.modf) 在结果在整数范围内时返回整数,否则返回浮点数。 #### math.abs(x) 返回 x 的绝对值。(integer/float) #### math.acos(x) 返回 x 的反余弦值(用弧度表示)。 #### math.asin(x) 返回 x 的反正弦值(用弧度表示)。 #### math.atan(y [, x]) 返回 y/x 的反正切值(用弧度表示)。 它会使用两个参数的符号来找到结果落在哪个象限中。 (即使 x 为零时,也可以正确的处理。) 默认的 x 是 1 , 因此调用 math.atan(y) 将返回 y 的反正切值。 #### math.ceil(x) 返回不小于 x 的最小整数值。 #### math.cos(x) 返回 x 的余弦(假定参数是弧度)。 #### math.deg(x) 将角 x 从弧度转换为角度。 #### math.exp(x) 返回 e^x 的值 (e 为自然对数的底)。 #### math.floor(x) 返回不大于 x 的最大整数值。 #### math.fmod(x, y) 返回 x 除以 y,将商向零圆整后的余数。 (integer/float) #### math.huge 浮点数 HUGE_VAL, 这个数比任何数字值都大。 #### math.log(x [, base]) 返回以指定底的 x 的对数。 默认的 base 是 e (因此此函数返回 x 的自然对数)。 #### math.max(x, ...) 返回参数中最大的值, 大小由 Lua 操作 < 决定。 (integer/float) #### math.maxinteger 最大值的整数。 #### math.min(x, ...) 返回参数中最小的值, 大小由 Lua 操作 < 决定。 (integer/float) #### math.mininteger 最小值的整数。 #### math.modf(x) 返回 x 的整数部分和小数部分。 第二个结果一定是浮点数。 #### math.pi π 的值。 #### math.rad(x) 将角 x 从角度转换为弧度。 #### math.random([m [, n]]) 当不带参数调用时, 返回一个 [0,1) 区间内一致分布的浮点伪随机数。 当以两个整数 m 与 n 调用时, math.random 返回一个 [m, n] 区间 内一致分布的整数伪随机数。 (值 n-m 不能是负数,且必须在 Lua 整数的表示范围内。) 调用 math.random(n) 等价于 math.random(1,n)。 这个函数是对 C 提供的位随机数函数的封装。 对其统计属性不作担保。 #### math.randomseed(x) 把 x 设为伪随机数发生器的"种子": 相同的种子产生相同的随机数列。 #### math.sin(x) 返回 x 的正弦值(假定参数是弧度)。 #### math.sqrt(x) 返回 x 的平方根。 (你也可以使用乘方 x^0.5 来计算这个值。) #### math.tan(x) 返回 x 的正切值(假定参数是弧度)。 #### math.tointeger(x) 如果 x 可以转换为一个整数, 返回该整数。 否则返回 nil 。 #### math.type(x) 如果 x 是整数,返回 "integer", 如果它是浮点数,返回 "float", 如果 x 不是数字,返回 nil 。 #### math.ult(m, n) 如果整数 m 和 n 以无符号整数形式比较, m 在 n 之下,返回布尔真否则返回假。
唐红
2025年12月15日 11:58
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码