第02章 类型和值

Lua 是动态语言,无类型定义,每个值都“携带”了它自身的类型信息。Lua 有八种基础类型:nil, boolean, number, string, userdata, function, thread, table. 下面分别介绍这八种基础类型:

nil

nil 表示一种无效值(non-value),即没有任何有效值的情况。变量在没有赋值以前,值都为 nil。如果想删除某个变量,直接把它的值赋成 nil 就可以了。

boolean

在 Lua 中,false 和 nil 表示 false,其余所有值都表示 true,包括数字0和空字串。这点和 C 有相似,但不同,具体 TODO

number

Lua 中并没有像其他语言区分整数和浮点数等等,它只有双精度浮点实数。如果要使用其他类型的数,可参考 luaconf.h 对 Lua 进行重编译。

数字书写形式,可以是普通写法,也可以是科学计数法,如:

    123, 1.23e+2

Lua 支持16进制的写法,在16进制的数字字面量前加上前缀0X0x就是一个合法的16进制数了,如:0Xf, 0xea34

但不知道它是否能表示8进制和2进制?TODO

string

string, 字符串型。Lua 处理字串很高效,长的字串几 M 也没问题。

字面量

可以使用单引号或双引号,但考虑程序的可读性,一般建议坚持使用一种风格,要么全部使用单引号,要么全部使用双引号。

转义符

Lua 也使用类似 C 的转义字符:

'\a' (bell)
'\b' (backspace)
'\f' (form feed)
'\n' (newline)
'\r' (carriage return)
'\t' (horizontal tab)
'\v' (vertical tab)
'\\' (backslash)
'\"' (quotation mark [double quote])
'\'' (apostrophe [single quote])

多行字串

这是 Lua 使用字串的方便之处,使用上类似块注释,以下提供两个例子:

hello = [[
    张三, 你好!
        展信佳,
        一别又是三秋, 时光荏苒, 思想无限, ...
           
                            此致
                                李四
                            2012.09.04   
]]

或:(如果字串中包含[[或]],则需在首尾添加任意数量的等号,首尾的等号数量相等)

hello = [===[
    张三, 你好!
        展信佳,
        一别又是三秋, 时光荏苒, 思想无限, ...
           
                            此致
                                李四
                            2012.09.04   
]===]

字串的连接

用 .. 而不是 +,如果用加号,那么系统会试图把字符串转成数字作加运算。

print("hello" + 1)         -- 会报错
print("hello" .. 1)        --> hello1
print(10 .. 24)            --> 1024
print(10.. 24)             -- 会报错,挨着10的点被当成小数点了。
print(10..24)              -- 会报错

避免数字和字串混合运算

数字和字串在一起混着运算,通常会出错,可以使用函数: tonumber tostring 把值转成数字或字串再进行运算。

长度操作符

Lua 的长度操作符是 #, 可以在字串前放置 # 获取字串的长度,如

print(#"hello") --> 5

userdata

userdata 表示自定义类型。userdata 类型可以将任意的 C 语言数据存储到 Lua 变量中。CAPI 或详细讨论。

function

在 Lua 中, 它是第一类值(first-class value),可以类似其他操作值一样的方式操作。函数是第一类值的特性也对 FP 提供了很好的支持,这点和 JavaScript 一致。Lua 可以调用自身编写的函数,也可以调用 C 函数(如何操作? 需要一个实例 TODO)。

可以使用函数 type 返回某个值的类型名称,如:

print(type("Hello, world!")) -->string
print(type(print)) --> function

问题1,Lua 的函数是第一类值,不知道其他 FP 都这样吗?TODO

问题2,function 作为 closure 的一个特例,这里如果说 closure 是 Lua 的一个基本类型,可行吗?TODO

thread

和系统程序(coroutine)有关,将在 TODO 中展开。

table

关联数组

它是一种关联数组(associative array),即 table 内的元素可以通过整数索引来访问,也可以使用字符键值的方式访问。

【定义方式1】

a = {x=10, y=20}
等价于
a = {}
a.x = 10
a.y = 20

【定义方式2】

{x = 0, y = 0}
等价于
{["x"]=0, ["y"]=0}

【定义方式3】

{"r", "g", "b"}
等价于
{[1]="r", [2]="g", [3]="b"}

table 中用于隔开元素的逗号也可以用分号替换,甚至可以混用。

疑问:JavaScript 的数组和对象字面量似乎也是关联数组(TODO 待证实),JavaScript 有普通数组也有关联数组,详细可参本站《JavaScript 的数组(TODO)》

匿名性

table 始终是匿名的,外界通过引用来访问它,当它没有任何引用时,gc 会自动回收它的。下面是来自书中的例子:

a = {}             -- 创建一个 table
a["x"] = 10        -- 这里也可以使用 a.x
b = a
print(b["x"])      -- 10
b["x"] = 20
print(a["x"])      -- 20
a = nil            -- 现在还有 b 在引用那个 table
b = nil            -- 再没有任何变量在引用 table 了,gc 将回收它

长度

类似求 string 的长度,table 的长度,也是通过 # 得到,如:

a = {}
a[100] = 2
print(#a) --> 0
print(table.maxn(a)) --> 100 当数组有空隙(hole)时,程序会认为它是一个结束标识符,会忽略空隙后的元素,如果在这种情况要处理所有的值,可使用函数 table.maxn 获取最大正索引数

另一个使用长度属性的比较综合的例子:

a = {1, 2, "3a"}
print(a[#a]) -- 打印列表的最后一个字符 3a

有关 Lua table 长度,早先在论坛上提了一个问题: http://bbs.luaer.cn/read-Lua-tid-3455-page-e.html#a,第三个回复还不错。

Index

table 的 index 可以从任意值开始,但默认和习惯上从1开始。虽然可以改成从0开始, 但不推荐, 否则很多库不能使用。

days = {[0]="Monday", "Tuesday", "Sunday"}; print(days[1]) ---> Tuesday

index 和 value 甚至互换:

days = {"星期一", "星期二", "星期三", "星期四", "星期五", }
revert_days = {}
for i, v in ipairs(days) do
    print(tostring(i) .. ":" .. tostring(v))
    revert_days[v] = i
end
for i, v in pairs(revert_days) do
    print(tostring(i) .. ":" .. tostring(v))
end

上段代码的输出为:

1:星期一
2:星期二
3:星期三
4:星期四
5:星期五
星期五:5
星期四:4
星期二:2
星期三:3
星期一:1