2.Types and Values
Lua是动态类型语言,变量不要类型定义.Lua中有8个基本类型:nil, boolean, number, string, userdata, function, thread, and table.
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string
变量没有预定义的类型,每一个变量都可能包含任一种类型的值.
print(type(a)) --> nil (`a' is not initialized)
a = 10
print(type(a)) --> number
a = "a string!!"
print(type(a)) --> string
a = print -- yes, this is valid!
a(type(a)) --> function
注意上面最后两行,我们可以使用function像使用其他值一样使用.一般情况下同一变量代表不同类型的值会造成混乱,最好不要用,特殊情况下可以带来便利,比如nil.
2.1 nil:Lua中特殊的类型,给全局变量负nil可以删除该变量.
2.2 booleans:两个取值false和true.但要注意Lua中所有的值都可以作为条件.在控制结构的条件中除了false和nil为假,其他值都为真.所以Lua认为0和空串都是真.
2.3 numbers:表示实数,Lua中没有整数.一般有个错误的看法CPU运算符点数比整数慢.事实不是如此,用实数代替整数不会有什么误差(除非数字大于100,000,000,000,000).Lua的numbers可以处理任何长整数不用担心误差.你也可以在编译Lua的时候使用长整型或者单精度符点型代替numbers.数字常量的例子:
4 0.4 4.57e-3 0.3e12 5e+20
2.4 lua 是8位字节,所以字符串可以包含任何数值字符,包括嵌入的0。 这意味着你可以存储任意的2进制数据在一个字符串里.Lua中字符串是不可以修改的,你可以创建一个新的变量存放你要的字符串,如下:
a = "one string"
b = string.gsub(a, "one", "another") -- change string parts
print(a) --> one string
print(b) --> another string
string和其他对象一样,Lua自动进行内存分配和释放,一个string可以只包含一个字母也可以包含一本书,Lua可以高效的处理长字符串,1M的string在Lua中是很常见的.
可以使用单引号或者双引号表示字符串
a = "a line"
b = 'another line'
为了风格统一,最好使用一种,除非两种引号嵌套情况.对于字符串中含有引号的情况还可以使用转义符\来表示.Lua中的转义序列有:
\a bell
\b back space
\f form feed
\n newline
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\" double quote
\' single quote
\[ left square bracket
\] right square bracket
例子:
> print("one line\nnext line\n\"in quotes\", 'in quotes'")
one line
next line
"in quotes", 'in quotes'
> print('a backslash inside quotes: \'\\\'')
a backslash inside quotes: '\'
> print("a simpler way: '\\'")
a simpler way: '\'
还可以在字符串中使用\ddd(ddd为三位十进制数字)方式表示字母.
"alo\n123\"" 和 '\97lo\10\04923"'是相同的
还可以使用[[...]]表示字符串.这种形式的字符串可以包含多行,可以嵌套,不会解释转义序列,如果第一个字符是换行符会被自动忽略掉.这种形式的字符串用来包含一段代码是非常方便的.
page = [[
<HTML>
<HEAD>
<TITLE>An HTML Page</TITLE>
</HEAD>
<BODY>
<A HREF="Luahttp://www.lua.org">Lua</A>
[[a text between double brackets]]
</BODY>
</HTML>
]]
io.write(page)
运行时,Lua会在string和numbers之间自动进行类型转换,当一个字符串使用算术操作符时,string会被转成数字.
print("10" + 1) --> 11
print("10 + 1") --> 10 + 1
print("-5.3e - 10"*"2") --> -1.06e-09
print("hello" + 1) -- ERROR (cannot convert "hello")
反过来,当Lua期望一个string而碰到数字时,会将数字转成string .
print(10 .. 20) --> 1020
..在Lua中是字符串连接符,当在一个数字后面写..时必须加上空格防止被解释错.
尽管字符串和数字可以自动转换,两者是不同的,像 10 == "10"这样的比较永远都是错的.如果需要显式将string转成数字可以使用函数tonumber(),如果string不是正确的数字该函数返回nil.
line = io.read() -- read a line
n = tonumber(line) -- try to convert it to a number
if n == nil then
error(line .. " is not a valid number")
else
print(n*2)
end
反之,可以调用tostring()将数字转成字符串,这种转换一直有效:
print(tostring(10) == "10") --> true
print(10 .. "" == "10") --> true
2.5 tables
Lua的tables实现了关联数组,关联数组指不仅可以通过数字检索数据,还可以通过别的类型的值检索数据.Lua中除了nil外的类型都可以作为tables的索引下标.tables是Lua主要的也是唯一的数据结构,我们可以通过他实现传统数组, 符号表, 集合, 记录(pascal), 队列, 以及其他的数据结构.Lua的包也是使用tables来描述的,io.read意味着调用io包中的read函数,对Lua而言意味着使用字符串read作为key访问io表.
Lua中tables不是变量也不是值而是对象.你可以把tables当作自动分配的对象,程序中只需要操纵表的引用(指针)即可.Lua中不需要声明表,使用最简单的{}表达式语句即可创建表.
a = {} -- create a table and store its reference in `a'
k = "x"
a[k] = 10 -- new entry, with key="x" and value=10
a[20] = "great" -- new entry, with key=20 and value="great"
print(a["x"]) --> 10
k = 20
print(a[k]) --> "great"
a["x"] = a["x"] + 1 -- increments entry "x"
print(a["x"]) --> 11
表是匿名的,意味着表和持有表的变量没有必然的关系.
a = {}
a["x"] = 10
b = a -- `b' refers to the same table as `a'
print(b["x"]) --> 10
b["x"] = 20
print(a["x"]) --> 20
a = nil -- now only `b' still refers to the table
b = nil -- now there are no references left to the table
当程序中不再引用表时,这个表将被删除,内存可以重新被利用.表可以使用不同的索引类型存储值.索引大小随着表中元素个数增加而增加.
a = {} -- empty table
-- create 1000 new entries
for i=1,1000 do a[i] = i*2 end
print(a[9]) --> 18
a["x"] = 10
print(a["x"]) --> 10
print(a["y"]) --> nil
最后一行,表对应的域没有被初始化所以为nil,和全局变量一样,Lua的全局变量存储正是使用表来存储的.
可以使用域名作为索引下表访问表中元素,Lua也支持a.name代替a["name"],所以我们可以用更清晰的方式重写上面的例子:
a.x = 10 -- same as a["x"] = 10
print(a.x) -- same as print(a["x"])
print(a.y) -- same as print(a["y"])
两种方式可以混合使用,对于Lua来说,两种方式相同,但对于读者来说单一的风格更易理解.
常见的错误:混淆a.x 和a[x];第一种表示a["x"],即访问域为字符串"x"的表中元素,第二种表示使用变量x作为索引下标访问表中元素.
a = {}
x = "y"
a[x] = 10 -- put 10 in field "y"
print(a[x]) --> 10 -- value of field "y"
print(a.x) --> nil -- value of field "x" (undefined)
print(a.y) --> 10 -- value of field "y"
只要使用整数作为索引下标就可以表示传统的数组了,不需要指定数组大小:
-- read 10 lines storing them in a table
a = {}
for i=1,10 do
a[i] = io.read()
end
当遍历数组元素时,第一个没有初始化的元素返回nil,可以用这个当作数组下标的边界标志.可以用下面的代码打印出上个例子读入的行:
-- print the lines
for i,line in ipairs(a) do
print(line)
end
既然可以使用任意值作为表的下标,你可以以任何数字作为数组下标的开始,但是Lua中一般以1开始而不是0(c语言).Lua标准库也是以这个设计的.
有一点需要特别注意,否则你的代码中可能引入很多难以发现的bug.因为我们可以使用任意类型的值作为索引下标,要注意:number 0 和string "0"是不同的,同样strings "+1", "01", and "1"也是不同的.
i = 10; j = "10"; k = "+10"
a = {}
a[i] = "one value"
a[j] = "another value"
a[k] = "yet another value"
print(a[j]) --> another value
print(a[k]) --> yet another value
print(a[tonumber(j)]) --> one value
print(a[tonumber(k)]) --> one value
当对你检索的类型有疑问时,请使用显示类型转换.
2.6 Functions
函数是第一类值(和其他变量相同),意味着函数可以存储在变量中,可以作为函数的参数,也可以作为函数的返回值.这个特性给了语言很大的灵活性:一个程序可以重新定义函数增加新的功能或者为了避免运行不可靠代码创建安全运行环境而隐藏函数,另外这个特性在Lua实现面向对象中也起了重要作用.
Lua可以调用lua或者C实现的函数,Lua所有标准库都是用C实现的.标准库包括string库,table库,I/O库,OS库,算术库,debug库.
2.7 Userdata and Threads
userdata 可以将C数据存放在Lua变量中,userdata 在Lua中除了负值和相等比较外没有预定义的操作.userdata 用来描述应用程序或者使用C实现的库创建的新类型.例如:标准I/O库用来描述文件.下面在C API章节中我们将详细讨论.
在第九章讨论协同操作的时候,我们介绍线程.