概述
Lua是一种动态类型语言 (Dynamically-typed Language),在Lua中可以直接使用变量而无需声明,并且使用的变量会随着环境自动改变类型。看到这儿,可能你心中会有以下的问题:
Lua中的变量到底是如何自动随环境改变类型的呢?
你不妨带着这个问题接着看下去。
Lua中一共有8种基本类型,不过日常开发中我们只需掌握其中7种即可,这8种分别是:
- nil (空)
- boolean (布尔)
- number (数字)
- string (字符串)
- table (表)
- function (函数)
- thread (线程)
- userdata (用户数据)
nil
nil的含义是 “零,空”,用来表示无效值 (non-value)。nil是一种类型,但它只有nil这一个值。这句话初看有些古怪🤔,且看代码:
print(none) --> nil
print(nil) --> nil
-- 其中两个减号后直到行末部分为注释,用-->表示输出结果。
第1行我们输出一个在此之前从来没有使用过的变量,结果是nil,说明我们没有给名为none的变量赋过值,注意在Lua中,👉任何没有赋过值的变量默认值都为nil。
第2行我们输出nil,发现它的结果也是nil!我们从第1行的输出结果可知,nil是一个特殊的值,用来表示不存在的变量;而第2行的输出结果告诉我们,类型nil的值也是nil。所以才会说nil是一种只有一个nil值的类型。
nil与其他语言 (如C语言) 中的null在拼写上很相近,在上一篇的Lua介绍中我们知道了Lua语言是通过标准C语言实现的,那么作者Roberto为什么特意使用这样一个相近却不同的概念呢?😕
原因有二:
- nil是一个类型;
- nil在条件判断中被视为false。
boolean
与其他语言相同,boolean类型具有2个值,true和false。但是在Lua中,除了false和nil之外的所有值都视为真,所以 0
和空字符串 ""
都被视为真。
逻辑运算符
Lua中有三个逻辑运算符:and、or和not。由于 “只有false和nil值为假” 这一特性,使得它们在实际使用中具有无比的灵活性。
and和or运算符具有短路特性,如果and的第一个操作数为false,则直接返回第一个操作数;如果or的第一个操作数为true,则直接返回第一个操作数。且看示例:
print(0 and 5) --> 5
print(nil and true) --> nil
print(0 or 5) --> 0
print(nil or true) --> true
Lua中有两个惯用写法非常重要✔:
x = x or v
当x不是false时,它的作用是当x未被初始化时,将其默认值设为v,相当于if not x then x = v end
;a and b or c
当b不是false时,他的作用等价于C/C++中的三目运算符a ? b : c
,例如(x > y) and x or y
返回x与y中的较大者。
number
顾名思义,该类型为数值类型,任何具有数值含义的变量都是number类型,且看示例:
print(type(2)) --> number
print(type(2.33)) --> number
print(type(2.33e23)) --> number
print(type(233^23)) --> number
算术运算符
+
:加号;-
:减号、取反;*
:乘号;/
:除号;//
:floor除号,该运算符为Lua独有,👉floor除法会对得到的商向负无穷取整,从而保证结果是一个整数。且看示例:print(3 // 2) --> 1 print(-9.2 // 2) --> -5
%
取模符号。示例:x = 3.1415927 print(x - x % 0.01) --> 3.14
string
对于Lua的字符串类型,有几点知识点:
- Lua中的字符串是不可变值 (immutable value)。不能在原字符串上修改,只能新建字符串来达到修改的目的;
- 用长度操作符
#
可以获得字符串的长度:print(#"good bye") --> 8
- 连接操作符两个点
..
用来进行字符串的连接:print("Hello " .. "World") --> Hello World
- 字符串可以使用一对双引号或者单引号:
a = "a line" b = 'another line' c = 'left, "inline", right' -- 在单引号中的双引号不需要转义
- 多行字符串用一对中括号包围:
page = [[ <html> <head> <title>Just a HTML Page</title> </head> </html> ]]
- 若多行字符串中存在中括号,可以在包围字符串的中括号对中插入相同数量的等号
=
:table = [==[ a = {} a.[1] = "first" a.[2] = "second" a.[3] = "third" ]==]
table
表是Lua中非常强大的数据结构,也是最重要的一个类型。
表由一个个键值对组成,同一个表中存储的值可以有不同类型的索引 (键),也可以有不同类型的值,并且可以按需增长以容纳新的元素。
a = {}
x = "y"
a[x] = 10
print(a[x]) --> 10
print(a.x) --> nil
print(a.y) --> 10
上述示例中,第3行代码的作用是添加键值对(“y”, 10),访问表中的元素有两种方法:
- 中括号
[]
:适用于任何类型的键; - 点号
.
:只适用于键为字符串类型,a.name
等价于a.["name"]
。
function
到目前为止我们接触最多的函数就是 print
了,函数最显著的特点就是使用时需要带上一对括号 ()
,括号中可以传入一些参数,调用一个函数后它也可能会返回一些值。例如:
print(type(1)) --> number
print(type(type(1))) --> string
可见,函数type的返回值是代表其参数类型的字符串。
thread
虽然Lua中有名为thread (线程) 的类型,但实际上它并不是线程,而是协程 (coroutine)。
虽然协程与线程差不多,都拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。但是线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起 (suspend) 时才会暂停。
那为啥作者要指鹿为马,偏偏给协程取名线程呢?这我真不知道,你问作者吧🤷♀️
关于协程,我会在之后的博客中详细探讨的。
userdata
userdata是一种用户自定义数据,用于表示一种由应用程序或C/C++语言库所创建的类型,可以将任意C/C++的任意数据类型的数据 (通常是struct和指针) 存储到Lua变量中调用。一般的用户不需要了解该种类型。
解答开篇
在开篇的时候我抛出了一个疑问:Lua中的变量是如何自动改变类型的?
首先,Lua中的变量是不需要声明就可以直接赋值使用的,而对一个已赋值的变量还可以赋其他类型的值,这时该变量的类型会自动改变为它当前存放的值的类型。
a = 1
print(type(a)) --> number
a = "it is a string"
print(type(a)) --> string
a = { b = 2.33 }
print(type(a)) --> table