Kong网关:入门、实战与进阶
上QQ阅读APP看书,第一时间看更新

3.2 数据类型

类似于Python、JavaScript,Lua是动态类型语言。它本身没有专门定义类型的语法,只有值才有类型。在Lua语言中,所有的值都是“一等公民”,这意味着所有的值均可保存在变量中,或者当作参数传递给其他函数,又或者作为返回值。Lua语言总共有8种数据类型:nil、boolean、number、string、function、userdata、thread和table。下面会对它们逐一介绍。

注意

静态类型语言和动态类型语言

动态类型语言是指在运行期间才去做数据类型检查的语言。也就是说,在用动态类型语言编程时,永远不用给任何变量指定数据类型。该语言会在用户第一次赋值给变量的时候,在程序内部将数据类型记录下来。

静态类型语言与动态类型语言刚好相反,其是在编译期间检查数据类型的。也就是说,在编写程序的时候就要声明所有变量的数据类型。C/C++是静态类型语言的典型代表。其他的静态类型语言还有C#、Java等。

静态类型语言运行时因为只需运行目标程序,所以运行速度快,但是在编码时要注意变量类型。动态类型语言灵活性高,但当代码量太大时,其不如静态类型语言稳定。所以开发大型项目时,要优先选用静态类型语言。

注意

程序设计语言中的一等、二等、三等公民。

一等公民:可以作为参数传递,也可以从子程序中返回,还可以赋值给变量。

二等公民:可以作为参数传递,但是不能从子程序中返回,也不能赋值给变量。

三等公民:值作为参数传递都不行。

此处再多做一下引申,函数为“一等公民”是函数式编程的基础。

1.空类型(nil)

空类型是值为nil的类型。其通常用来表示一个有意义的值不存在时的状态,可以类比于C、Java中的null。


> print(type(nil))
nil

注意

库函数type可以返回当前给定变量的数据类型,类似的有Python的type函数或者Node.js的typeof函数。

2.布尔类型(boolean)

布尔类型是值为true与false的类型。其概念与在其他编程语言中的概念保持一致。nil和false都会导致条件判断为假;而其他任何值都表示条件判断为真。此处示例打印了值为true和false的类型。


> print(type(true))
boolean
> print(type(false))
boolean

3.数值类型(number)

数值类型表示整数和实数(浮点数),包括十六进制和科学计数法,示例如下所示。


> print(type(42))
number
> print(type(3.1415926))
number
> print(type(6.62607015e-34))
number

4.字符串类型(string)

字符串由一对双引号或单引号来表示,表示一个不可变的字节序列。Lua中不支持直接修改某个字符串中的字符,只能按照修改的条件重新创建一个字符串,例如:


> print(type("Hello World"))
string
> str1 = 'Hello World'
> print(str1)
Hello World
> str2 = string.gsub(str1, 'World', "Lua")
> print(str2)
Hello Lua

示例中,string.gsub方法可以替换字符串中的元素。除此之外,还有很多实用的字符串方法,3.7节会做重点介绍。

5.函数类型(function)

一个函数定义是一个可执行的表达式。执行结果是一个类型为函数(function)的值。当使用Lua预编译一个代码块时,代码块作为一个函数。Lua的函数类型有以下特点。

1)可以将函数作为参数传递给函数:


> increment = function(num, step)
>>   return num + step
>> end
> function addByIncrement(num1, num2, step, increment)
>>   return increment(num1, step) + increment(num2, step)
>> end
> print(addByIncrement(1, 2, 3, increment))  --->9
9

2)可以有多个返回值:


> s, e = string.find('Hello Lua World', 'Lua')
> print(s, e)
7   9

3)允许有可变参数(在函数参数列表中使用“...”表示函数可以有可变参数):


> function add(...)
>> local s = 0
>>   for i, v in ipairs{...} do   --> {...} 表示一个由所有变长参数构成的数组
>>     s = s + v
>>   end
>>   return s
>> end
> print(add(1,2,3,4,5))  --->15
15

6.用户数据类型(userdata)

用户数据类型允许将C程序中的数据保存在Lua变量中。用户数据分为两类:完全用户数据,指由Lua管理的内存对应的对象;轻量用户数据,指简单的C指针。

在Lua中,用户数据除了赋值与相等性判断之外,没有其他预定义操作。通过使用元表,用户可以给完全用户数据定义一系列操作,并且只能通过C API创建或者修改用户数据。这保证了数据仅被宿主程序所控制。

7.线程类型(thread)

线程类型表示一个独立的执行序列,用于实现协程。Lua线程与操作系统的线程毫无关系。Lua可以为所有系统,包括那些不支持原生线程的系统,提供协程支持。代码清单3-5使用Lua协程实现了经典的生产者-消费者示例。

程序清单3-5 生产者-消费者示例


-- 生产者-消费者示例
> local newProductor
>
> function productor()
>>   local i = 0
>>   while true do
>>     i = i + 1
>>     send(i)            --> 将生产的对象发送给消费者
>>   end
>> end
>
> function consumer()
>>   while true do
>>     local i = receive()    --> 从生产者那里得到对象
>>     print(i)
>>   end
>> end
>
> function receive()
>>   local status, value = coroutine.resume(newProductor)
>>   return value
>> end
>
> function send(x)
>>   coroutine.yield(x)        --> x表示需要发送的值。值返回以后,就挂起该协程
>> end
>
> newProductor = coroutine.create(productor)  --> 启动程序
> consumer()
1
2
3
4
5
6
7
8
...
...

coroutine.create方法可以用于创建协程。其唯一的参数是该协程的主函数。coroutine.create方法只负责新建协程并返回其句柄(一个thread类型的对象),而不会启动该协程。

coroutine.resume方法可以用于执行协程。第一次调用coroutine.resume时,第一个参数应传入coroutine.create返回的线程对象,然后协程从其主函数的第一行开始执行。传递给coroutine.resume的其他参数将作为协程主函数的参数传入。协程启动之后,将一直运行到它终止或让出执行权。

协程可能被两种方式终止运行:正常途径是主函数返回(显式返回或运行完最后一条指令);非正常途径是发生一个未被捕获的错误。对于正常终止,coroutine.resume将返回true,并接上协程主函数的返回值。当错误发生时,coroutine.resume将返回false和报错信息。

通过调用coroutine.yield可以使协程暂停执行,让出执行权。协程让出时,对应的最近coroutine.resume方法会立刻返回。在协程让出的情况下,coroutine.resume也会返回true,并加上传给coroutine.yield的参数。当重启同一个协程时,其会接着从让出点继续执行。此时,之前让出点处调用的coroutine.yield方法会返回,返回值为传给coroutine.resume的第一个参数之外的其他参数。

与coroutine.create类似,coroutine.wrap方法也可以用于创建协程。不同之处在于,它不返回协程本身,而是返回一个函数。传递给该方法的任何参数均当作coroutine.resume的额外参数。coroutine.wrap返回coroutine.resume的所有返回值,除了第一个返回值(布尔类型的错误码)。和coroutine.resume不同,coroutine.wrap不会捕获错误,而是将错误信息返给调用者。

8.表类型(table)

表是Lua中唯一的数据结构,也是非常灵活的数据结构。它可被用于表示普通数组、序列、符号表、集合、字典、图、树等。对于字典,Lua使用域名作为索引。Lua语言提供了a.name这样的语法糖来替代a["name"]写法。Lua提供了多种便利的方式来创建表。下面是表作为数组和字典的使用示例。

1)表作为数组的使用示例:


> array = {0,1,2,3,4,5}
> for key, value in pairs(array) do
>>   print(value)
>> end
0
1
2
3
4
5

2)表作为字典的使用示例:


> config = {system='linux', version='2.6'}
> config.user = "root"
> for key, value in pairs(config) do
>>   print(key..': '..value)
>> end
system: linux
user: root
version: 2.6

Lua语言通过表类型来抽象其他语言中的模块(Module)、包(Package)和对象(Object)等概念。