整型的引入是 Lua5.3 的一个重要标志。在精简 Lua 模式下(small lua),该模式使用 32 位整型和单精度浮点类型。
这篇文章将学习 Lua 的数值(number) 和字符串(string)类型。
可以使用科学计数法(一个可选的十进制部分外加一个可选的十进制指数部分)书写数值常量。具有十进制小数或指数的数值会被当做浮点型值,否则会被当做整型值。
整型值和浮点型值的类型都是 number ,它们之间可以相互转换。而且,具有相同算术值的整型值和浮点型值在 Lua 语言中都是相同的:
number
12345678
> type(1)number> type(1.00)number> 1 == 1.00true> 1 == 0.01e2true
如果的确需要区分整型值和浮点型值时,可以使用函数 math.type :
math.type
1234
> math.type(1)integer> math.type(1.00)float
123456
> math.type(1.00)float> math.type(1e0)float> math.type(1e2)float
Lua 也支持以 0x 开头的十六进制常量,而且支持十六进制的浮点数,这种十六进制浮点数由小数部分和以 p 或 P 开头的指数部分组成。在函数 string.format 中可以使用 %a 参数进行该格式的格式化输出。 这种格式可以保存所有浮点数的精度。而且比十进制转换速度更快 。
string.format
%a
12345678910
> 0xff255> 0x1A3419> 0x0.20.125> 0x1p-10.5> 0xa.bp242.75
对于 Lua5.3 引入的整型,主要建议是 开发人员要么选择忽略整型和浮点型二者之间的不同,要么就完整地控制每一个数值的表示 。因此,所有的算术运算符不论操作整型还是浮点型值,结果应该都是一样的。
开发人员要么选择忽略整型和浮点型二者之间的不同,要么就完整地控制每一个数值的表示
对于算术运算符 +、-、*、/、//(整除)、%(取模)、^(幂等) ,如果两个操作数都是整型,那么结果也是整型值,否则就是浮点型值。但是对于除法和幂等运算不遵循上述规则,为了避免两个整型值相除(不一定能整除)和两个浮点型值相除导致不一样的结果,除法运算操作操作的永远是浮点数且产生浮点型结果。
+、-、*、/、//(整除)、%(取模)、^(幂等)
12345678910111213
> 2 + 46> 2.0 + 4.06.0> 2 + 4 == 2.0 + 4.0true> 4 / 22.0> 4.0 / 2.02.0> 4 / 2 == 4.0 / 2.0true
和除法一样,幂等运算的操作数也永远是浮点型,例如 2^-2 其实就是执行除法运算。
2^-2
12345
> 2 ^ -20.25> 4 ^ 0.52.0
整除(floor) // 会将得到的商向负无穷取整,从而保证结果是一个整数。因此它就和其他算术运算符遵循同样的规则:如果操作数是整型,那么结果就是整型,否则就是浮点型值(其值是一个整数)。
//
> 3 // 21> 3.0 // 2.01.0> 3 // 2 == 3.0 // 2.0true
取模运算的定义为 a % b == a - ((a // b) * b) 。对于整型操作数而言,取模运算结果的符号永远与第二个操作数的符号保持一致。对于实数类型的操作数而言,取模运算则有些不同, x - x % 0.01 恰好是 x 保留两位小数的结果, x - x % 0.001 恰好是 x 保留三位小数的结果。
a % b == a - ((a // b) * b)
x - x % 0.01
x - x % 0.001
> x = 3.1415927> x % 0.010.0015926999999998> x - x % 0.013.14
Lua 提供了以下关系运算符: <、>、<=、>=、==、~= ,这些关系运算符的结果都是 Boolean 类型:
<、>、<=、>=、==、~=
> 1 == nilfalse> nil == niltrue> 1 == nilfalse> nil == niltrue>> 1 == 1.0true> 1.0 == "1.0"false
Lua 提供了标准数学库 math,由一组标准的数学函数组成:包括取整函数、指数函数、最大/最小函数、三角函数等,同时也提供了常量 pi、huge(最大可表示数值,在大多数平台上代表 inf)。
函数 math.random 用于生成伪随机数,当不带参数调用时,返回一个 [0,1) 范围内均匀分布的伪随机实数。当使用整型值 n 为参数调用时,返回一个在范围 [1, n) 范围内的位随机整数。当使用两个整型值 l 和 u 为参数调用时,返回在 [l, u] 范围内的伪随机整数
[0,1)
[1, n)
l
u
[l, u]
函数 randomseed 用于设置伪随机数发生器的种子。程序启动时,系统固定使用 1 作为种子初始化伪随机数发生器,这样程序每次运行时都会生成相同的伪随机数序列。可以使用 math.randomseed(os.time()) 来使用当前系统时间作为种子初始化随机数发生器
1
math.randomseed(os.time())
floor 用于向负无穷取整、 ceil 用于向正无穷取整、 modf 用于向 0 取整(除了返回取整后的值以外,函数 modf 还会返回小数部分作为第二个结果)。如果想将数值 x 向最近的整数取整,可以使用 floor(x+0.5) 的方式
floor
ceil
modf
floor(x+0.5)
标准 Lua 使用 64 位来存储整型,其最大值为 2**63 - 1 。精简 Lua 使用 32 个比特位来存储整型值。数学库中定义了整型的最大值 math.maxinteger 和最小值 math.mininteger 。
2**63 - 1
math.maxinteger
math.mininteger
对于浮点数而言,标准 Lua 使用双精度。标准 Lua 使用 64 个比特位来表示所有数值,其中 11 位为指数,能表示的范围从 -10**308 ~ 10**308 。双精度浮点数对于大多数应用而言是足够大的。
-10**308 ~ 10**308
浮点型的值可以表示很大的范围,但是浮点型能够精确表示的整数范围为 [-2**53,2**53] 之间,在这个范围内,基本可以忽略整型和浮点型的区别,超出该范围之后,则应该谨慎地思考所使用的方式。
[-2**53,2**53]
0.0
2**53
math.tointeger
123456789101112131415161718192021
> 9007199254740991 + 0.0 == 9007199254740991true> 9007199254740992 + 0.0 == 9007199254740992true> 9007199254740993 + 0.0 == 9007199254740993false> 2 ^ 53 | 09007199254740992> math.type(2^53)float> math.type(2^53|0)integer> 9007199254740991.0 == 9007199254740991true> 9007199254740992.0 == 9007199254740992true> 9007199254740993.0 == 9007199254740993false> 9007199254740993.0 | 09007199254740992
Lua 中运算符优先级如下:
在二元运算符中,除了幂运算和连接操作符是右结合之外,其他运算符都是左结合的。当不能确定某些表达式的运算符优先级时,应该显式地用括号来指定所希望的运算次序。
2**63
3.0
3
字符串用于表示文本, Lua 语言中的字符串是一串字节组成的序列 。Lua 核心并不关心这些字节以何种方式编码文本:
Lua 中的字符串是不可变值,只能通过创建一个新的字符串的方式来达到修改目的。Lua 中的字符串也是自动内存管理的对象之一,即 Lua 语言会负责字符串内存的分配和释放。
#
..
1234567891011121314
> a = "hello"> print(#a)5> "hello".."world"helloworld> "3"..232> 3..2stdin:1: malformed number near '3..2'> a = "hello"> a.."world"helloworld> ahello
使用一对双引号或者单引号来声明字符串常量。单引号、双引号两者是等价的,区别在于转义的时机:使用双引号声明的字符串中出现单引号时,单引号可以不用转义。使用单引号声明的字符串中出现双引号时,双引号可以不用转义。
Lua 字符串支持 C 语言风格的转义字符,例如 \n 等。也可以通过转义序列 \ddd 和 \xhh 来声明字符,其中 ddd 是由最多 3 个十进制数字组成的序列, hh 是由两个且必须是两个的十六进制数字组成的序列。
\n
\ddd
\xhh
ddd
hh
从 Lua5.3 开始,也支持使用转义序列 \u{h..h} 来声明 UTF-8 字符,花括号中可以支持任意有效的十六进制。
\u{h..h}
可以使用一对 双括号 来声明长字符串/多行字符串常量,此时内容中的转义序列不会被转义。另外如果多行字符串的第一个字符是换行符,该换行符会被忽略。
双括号
当内容中本身含有双括号时,可以在两个左方括号之间加上任意数量的等号,例如 [===[ ,这样字符串常量只有在遇到了包含相同数量等号的两个右方括号时才会结束(本例中即 ]===] 。Lua 语法扫描器会忽略所含等号数量不相同的方括号。通过选择恰当数量的等号,就可以无需修改原字符串的情况下声明任意的字符串常量。对于注释,这种机制同样有效,例如使用 --[=[ 和 ]=] 来进行长注释,从而降低对内部已经包含注释的代码进行注释的难度。
[===[
]===]
--[=[
]=]
从 Lua5.2 开始引入了转义序列 \z ,该转义符会跳过其后的所有空白字符,直到遇到第一个非空白字符。
\z
Lua 语言运行时提供了数值与字符串之间的自动转换:
> print(10 .. 20)1020> print(10..20)stdin:1: malformed number near '10..20'> print(10.."20")stdin:1: malformed number near '10..'
注意,当数值之后紧接着使用字符串连接时,必须使用空格将它们分开,否则 Lua 语言会将第一个点当成小数点 。
使用 tonumber 可以显式地将一个字符串转换成数值。默认该函数使用十进制,但也可以指定二进制到三十六进制之间的任意进制。使用 tostring 可以将数值转换为字符串。
tonumber
tostring
12345678910111213141516171819
> tonumber(" -3")-3> tonumber(" 10e4 ")100000.0> tonumber("0x1.3p-4")0.07421875> tonumber("1010", 2)10> tonumber("ff", 16)255> tonumber("989", 8)nil> tostring(10)10> tostring(0x16)22> type(tostring(10))string
注意,比较操作符不会对操作数进行强制类型转换。当比较运算符中混用了字符串和数值时,Lua 会抛出异常。
1234567
> 2 < "2"stdin:1: attempt to compare number with stringstack traceback: stdin:1: in main chunk [C]: in ?> 2 == "2"false
Lua 语言解释器本身处理字符串的能力有限。Lua 语言处理字符串的完整能力来自其字符串标准库:
string.len(s)
#s
string.rep(s, n)
string.reverse()
string.lower(s)
string.uppper(s)
string.sub(s, i, j)
string.char 和 string.byte
string.char
string.byte(s, i)
string.byte(s, i, j)
{string.byte(s, 1, -1)}
string.find
string.gsub
12345678910111213141516171819202122232425
> string.lower("Hello")hello> string.upper("Hello")HELLO> string.char(65, 66, 67)ABC> string.byte("ABC", 2)66> string.byte("ABC", -1)67> string.format("%s %d %d", "1", 10, -1)1 10 -1> string.format("%x", 31)1f> string.format("<%s>%s<%s>", "h1", "hello", "h1")<h1>hello<h1>> string.find("hello world", "wor")7 9> string.find("hello world", "war")nil> string.gsub("hello world", "wor", "xxx")hello xxxld 1
也可以可以使用冒号操作符像调用字符串的一个方法那样来调用字符串标准库中的所有函数 :
> s = "ABC"> string.sub(s, 1, 2)AB> s:sub(1, 2)AB
UTF-8 使用变长的多个字节编码一个 Unicode 字符。首先字符串标准库中的函数 reverse、upper、lower、byte、char 不适用于 UTF-8 字符串,因为他们针对的都是一字节字符。 format、rep 适用于 UTF-8 字符串(格式选项 %c 除外,该格式选项针对一字节字符)。 len 、 sub 可以用于 UTF-8 字符串,其中的索引以字节为单位而不是以字符为单位。
reverse、upper、lower、byte、char
format、rep
%c
len
sub
从 Lua5.3 开始,Lua 引入了用于操作 UTF-8 编码的 Unicode 字符串的标准库:
utf8.len
utf8.char
utf8.codepoint
string.byte
utf8.offset
utf8.codes
> utf8.len('中国')2> utf8.codepoint('中国', 1, 3)20013> utf8.char(20013)中> utf8.offset('中国', 2)4> for i, c in utf8.codes('中国') do>> print(i, c)>> end1 200134 22269
Lua 并没有提供其他 Unicode 字符相关的特性,因为 Unicode 本身的复杂性,想要支持完整地 Unicode 需要巨大的表,而这与 Lua 精简的大小相矛盾。因此对于这些特殊需求来说,最好就是选择外部库。
十六进制浮点数