2.2 lua 基础数据类型

lua 是一门极其精简的语言,内置类型只要nil,boolean,number等三种基本类型,以及string,table等二种组合类型。没有C/C++ 等语言中的classstruct等复杂类型,这些类型可以通过table来实现。同时,不同于其他语言(如C/C++和java),function在lua中也作为一个一级类型。

nil

nil 是一种类型,Lua 将 nil 用于表示“无效值”。

  1. 一个变量在第一次赋值前的默认值是 nil
  2. nil 赋予给一个全局变量就等同于删除它.
  3. openresty 中提供了另一种表示空值的类型ngx.null.

nil在lua中有其特殊意义,如果一个变量被设置为nil,就等于说该变量未定义,与无穷无尽的其他未定义的变量一样。那么,如果把redis查询为空的结果设置为nil,就无法把”查询为空”和“未定义”区分开来了,例如在一个table中,一个key对应一个value,如果将该value设置为nil,则相当让key凭空消失,这显然是不合理的。所以必须用一个userdata类型的独特的值来表示这种查询为空,但又不等同于未定义的变量,例如ngx.null。同样的情况想必在sql的lua模块中也会出现,用来处理记录中键值查询为空的情况。
lua中神奇的nil了。nil是一种类型,该类型只有一个值,这个值就是nil。该值的作用只有一个,表示一个变量不存在。跟C\C++等常规语言不同,”不存在“跟空、0完全是两个概念。在C语言中,一个字符串如果为空,那么它就只有一个为0\nul结束符,如果对齐进行逻辑判断,则是假。但lua中,只要一个变量不是nil类型或者是boolean类型中的false,则对它进行逻辑判断,结果是真,即使该值是一个数字0,或者是一个空字符串。

boolean

布尔类型,可选值 true/false;Lua 中 nil 和 false 为“假”,其它所有值均为“真”。比如 0 和空字符串就是“真”;C 或者 Perl 程序员或许会对此感到惊讶。

number

Number 类型用于表示实数,和 C/C++ 里面的 double 类型很类似。可以使用数学函数math.floor( 向下取整) 和 math.ceil( 向上取整) 进行取整操作。

一般地,Lua 的 number 类型就是用双精度浮点数来实现的。值得一提的是,LuaJIT 支持所谓的“dual-number”( 双数) 模式,即 LuaJIT 会根据上下文用整型来存储整数,而用双精度浮点数来存放浮点数。另外,LuaJIT 还支持“长长整型”的大整数( 在 x86_64 体系结构上则是 64 位整数) 。例如

1
2
>print(9223372036854775807LL - 1)
9223372036854775806LL

string

Lua 中有三种方式表示字符串:

  1. 使用一对匹配的单引号。例:’hello’。
  2. 使用一对匹配的双引号。例:”hello”。
  3. 字符串还可以用一种长括号( 即[[ ]]) 括起来的方式定义。我们把两个正的方括号( 即[[)间插入 n 个等号定义为第 n 级正长括号。就是说,0 级正的长括号写作 [[ ,一级正的长括号写作 [=[,如此等等。反的长括号也作类似定义;举个例子,4 级反的长括号写作]====]。一个长字符串可以由任何一级的正的长括号开始,而由第一个碰到的同级反的长括号结束。整个词法分析过程将不受分行限制,不处理任何转义符,并且忽略掉任何不同级别的长括号。这种方式描述的字符串可以包含任何东西,当然本级别的反长括号除外。例:[[abc\nbc]],里面的 “\n” 不会被转义。

特别注意:Lua 的字符串是不可改变的值,不能像在 c 语言中那样直接修改字符串的某个字符,而是根据修改要求来创建一个新的字符串。Lua 也不能通过下标来访问字符串的某个字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>local str1 = 'hello world'
>local str2 = "hello world"
>local str3 = [["add\name",'hello']]
>local str4 = [=[string have a [[]].]=]
>local str5 = 'hello\nworld'
>local str6 = "hello\nworld"

>print(str1)
hello world
>print(str2)
hello world
>print(str3)
"add\name",'hello'
>print(str4)
string have a [[]]
>print(str5)
hello
world
>print(str6)
hello
world

特别注意: 从上面实例str5str6可以看出'"在lua中作用是一致的,二者中的\n这类转义字符都会被转义,这一点要区别一下shell中'"

在 Lua 实现中,Lua 字符串一般都会经历一个“内化”( intern) 的过程,即两个完全一样的Lua 字符串在 Lua 虚拟机中只会存储一份。每一个 Lua 字符串在创建时都会插入到 Lua 虚拟机内部的一个全局的哈希表中。 这意味着

  1. 创建相同的 Lua 字符串并不会引入新的动态内存分配操作,所以相对便宜( 但仍有全局哈希表查询的开销。
  2. 内容相同的 Lua 字符串不会占用多份存储空间。
  3. 已经创建好的 Lua 字符串之间进行相等性比较时是 O(1) 时间度的开销,而不是通常见到的 O(n),比较索引就可以了。

table

Table 类型实现了一种抽象的“关联数组”。“关联数组”是一种具有特殊索引方式的数组,索引通常是字符串( string) 或者 number 类型,但也可以是除 nil 以外的任意类型的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
local corp = {
web = "www.google.com", --索引为字符串,key = "web",
-- value = "www.google.com"
telephone = "12345678", --索引为字符串
staff = {"Jack", "Scott", "Gary"}, --索引为字符串,值也是一个表
100876, --相当于 [1] = 100876,此时索引为数字
-- key = 1, value = 100876
100191, --相当于 [2] = 100191,此时索引为数字
[10] = 360, --直接把数字索引给出
["city"] = "Beijing" --索引为字符串
} p
rint(corp.web) -->output:www.google.com
print(corp["telephone"]) -->output:12345678
print(corp[2]) -->output:100191
print(corp["city"]) -->output:"Beijing"
print(corp.staff[1]) -->output:Jack
print(corp[10]) -->output:360

在内部实现上,table 通常实现为一个哈希表、一个数组、或者两者的混合。具体的实现为何种形式,动态依赖于具体的 table 的键分布特点。

function

在 Lua 中,函数:

  1. 也是一种数据类型,
  2. 函数可以存储在变量中,
  3. 可以通过参数传递给其他函数,
  4. 还可以作为其他函数的返回值
1
2
3
4
5
6
7
8
9
10
11
12
local function foo()
print("in the function")
--dosomething()
local x = 10
local y = 20
return x + y
end
local a = foo --把函数赋给变量
print(a())
--output:
in the function
30

有名函数的定义可以理解为是匿名函数对变量的赋值。

1
2
function foo()
end

可以写成如下形式:

1
2
foo = function ()
end

类似地,

1
2
local function foo()
end

等价于

1
2
local foo = function ()
end