Lua 数据类型    

nil                      这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。

boolean            包含两个值:false和true。

number            表示双精度类型的实浮点数

function            由 C 或 Lua 编写的函数

string                字符串由一对双引号或单引号来表示

thread              表示执行的独立线路,用于执行协同程序

table    Lua      中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。

userdata         表示任意存储在变量中的C数据结构


nil类型

对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉,

tab1 = { key1 = "val1", key2 = "val2", "val3" }for k, v in pairs(tab1) do
   print(k .. " - " .. v)end
tab1.key1 = nilfor k, v in pairs(tab1) do
   print(k .. " - " .. v)end


boolean类型

boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是"假",其他的都为"真":


number类型

Lua 默认只有一种 number 类型 -- double(双精度)类型


string类型

也可以用 2 个方括号 "[[]]" 来表示"一块"字符串。


html = [[
<html>
<head></head>
<body>
   <a href="
http://www.w3cschool.cc/">w3cschool菜鸟教程</a>
</body>
</html>
]]
print(html)

打印结果为

<html>
<head></head>
<body>
   <a href="
http://www.w3cschool.cc/">w3cschool菜鸟教程</a>
</body>
</html>

在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字:

使用 # 来计算字符串的长度,放在字符串前面,如下实例:


table(表)

对table的索引可以使用[].有些情况下也可以使用.操作

t[i]


t.i 仅当索引是字符串类型时可以使用

table一般完整写法

local a = {["x"] = 12, ["mutou"] = 99, [3] = "hello"}

print(a["x"]);

对于字符串下标,我们可以省略方框和双引号,但是数字下标不可以

local a = {x = 12, mutou = 99, [3] = "hello"}

print(a.x);

大家习惯了数组,用数字下标

local a = {[1] = 12, [2] = 43, [3] = 45, [4] = 90}


gettable_event(t,i) 采用索引本质上是方法的调用


userdata(自字义内容)

userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。


Lua 变量

lua中的变量有3种,全局变量,局部变量和表中的域

函数外的变量默认为全局变量,除非用local声明,函数内变量和函数参数默认为局部变量

    -- test.lua 文件脚本a = 5               
    -- 全局变量local b = 5     -- 局部变量function joke()
    c = 5           -- 局部变量
    local d = 6 -- 局部变量endprint(c,d)      --> nil nildo 
    local a = 6 -- 局部变量
    b = 6           -- 全局变量
    print(a,b); --> 6 6endprint(a,b)      --> 5 6

lua中可以通过以下方式交换俩个变量的值(lua可以对多个变量赋值,变量列表和值例表用,隔开)

x,y = y,x

当变量个数和值不一致时,lua采取以变量个数为准的策略

a. 变量个数 > 值的个数             按变量个数补足nil
b. 变量个数 < 值的个数             多余的值会被忽略

Lua 循环

while循环             在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。

for循环              重复执行指定语句,重复次数可在 for 语句中控制。

lua repeat...util循环        重复执行循环,直到 指定的条件为真时为止

Lua 字符串

lua提供了很多字符串操作

string.upper(argument)     字符串全部转为大写字母

string.lower(argument);         字符串全部转换成小写字母

string.gsub(mainstring,findstring,replacestring,num)        mainstrig中的findstring都转换成replacestring,num表示转换几次

string.strfind(str,substr,[init,[end]])        在一个指定的字符串中搜索指定的内容,返回开始位置和终止位置,第三个参数为可选参数表示init起始位置 ,end不知道

string.reverse(arg)            反转字符串

string.fomat()                 返回一个类似print的字符串,

str = string.format("the value is %d",3)
print(str)

string.char(arg)  string.byte(arg,[init]) 根据ascii码转换 char转换数字为字符 byte转换字符为数字 init表示从哪个位置开始

string.len(arg)  计算字符串的长度,也可以通过#运算符来取得

string.rep(arg,n)    返回字符串的n个拷贝  

多维数组

初始化

array = {}
for i = 1,3 do
    array[i] = {}
    for j = 1,3 do
        array[i][j] = 8
    end
end


Table 操作

table.concat(table[sep,[,start,[,end]]])    table.concat参数中指定table的数组部分从start位置到end位置的所有元素,元素间以指定的分割符sep隔开

fruits = {"banana","orange","apple"}
-- 返回 table 连接后的字符串
print("连接后的字符串 ",table.concat(fruits))
-- 指定连接字符
print("连接后的字符串 ",table.concat(fruits,", "))
-- 指定索引来连接 
tableprint("连接后的字符串 ",table.concat(fruits,", ", 2,3))

table.insert(table,[pos],value)    插入元素,无pos参数插入最后

table.remove(table,[pos])    删除元素,无pos参数删除最后

fruits = {"banana","orange","apple"}
-- 在末尾插入table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])
-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")print("索引为 2 的元素为 ",fruits[2])
print("最后一个元素为 ",fruits[5])table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5]) --这人为nil

table.sort(table,[comp])    按升序排列

fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) do
    print(k,v)
endtable.
sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do
    print(k,v)
end

Lua 模块与包(其实是table)

模块相当于封闭库,可以将一些公用的代码放到这个里面,以Api接口的形式在其它地方调用,有利于代码的重用和降低耦合度

模块是由变量,函数等组成的Table,因些创建一个模块很简单,就是创建一个Table,然后把需要导入的常量,函数放入其中,最后返回table

--定义一个model模块,注意文件名也必须是model
modul = {}
modul.constant = "constnum"
function modul.fun1()
	print("public function")
end

function fun2()
	print("prive function")
end

function modul.fun3()
	fun2()
end

return modul

由上可知,模块的结构就是一个 table 的结构,因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。

上面的 func2 声明为程序块的局部变量,即表示一个私有函数,因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用.

require("modul")
print(modul.constant)
modul.fun1()
--modul.fun2() 错误,私有的
modul.fun3()

别一个文件中通过require加载模块
wKioL1cwywvhrGiQAAAO15CJ-w8880.png

加载机制

对于自定义的模块,模块文件不是放在哪个目录都行的,函数require有它自己的路径加载策略,它会尝试从lua或者C库中加载模块

reuire用于尝试搜索lua文件的路径是存放在package.path中,它lua启动后会通过LUA_PATH的值来初始这个环境变量,如果没有找到,则会默认一个路径来初始化

当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

文件路径以 ";" 号分隔,最后的 2 个 ";;" 表示新加的路径后面加上原来的默认路径。

接着,更新环境变量参数,使之立即生效。

source ~/.profile

这时假设 package.path 的值是

/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua

那么调用 require("module") 时就会尝试打开以下文件目录去搜索目标。

/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua

C 包

Lua和C是很容易结合的,使用C为Lua写包。

与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。

Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:

local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")

loadlib函数加载指定的库并且连接到Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为Lua的一个函数,这样我们就可以直接在Lua中调用他。

如果加载动态库或者查找初始化函数时出错,loadlib将返回nil和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:

local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))   
f()  -- 真正打开库

一般情况下我们期望二进制的发布库包含一个与前面代码段相似的stub文件,安装二进制库的时候可以随便放在某个目录,只需要修改stub文件对应二进制库的实际路径即可。

将stub文件所在的目录加入到LUA_PATH,这样设定后就可以使用require函数加载C库了。

Lua 元表(Metatable)

lua中Table我们可以通过key来得到Value,但是无法对俩个表进行操作

因此lua提供了元表(metatable)允许我们改变Table的行为,每个行为关联对应的元方法


例如使用元表我们可以定义lua如何计算两个Talble相加操作a+b,当lua试图对俩个表相加时,先检查两者之一是否有元表,之后检查是否有_add的字段,若找到则调用对应的值

"_add"等即时字段,其对应的值(往往是一个函数或者table)就是"元方法"

两个重要的方法来处理元表

setmetatable(table,metatable)给指定table设定元表(metatable),如果元表中存在__metatable建值,失败

getmetatable(table)返回对像的元表

mytable = {}                          -- 普通表 
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表


以上代码可以写成一行

mytable = setmetatable({},{})

以下返回对像元素

getmetatable(mytable)                 -- 这回返回mymetatable

__index元方法

这是metatable最常用的键

当你通过键来访问table的时候,如果这个键没有值,那么lua会寻找table的metatable(假定有metatable)中的__index键,


如果__index包含一个函数的话,lua就会调用这个函数,table和键会作为参数传递给函数

mytable = setmetatable({key1 = "value1"}, {
  __index = function(mytable, key)
    if key == "key2" then
      return "metatablevalue"
    else
      return nil
    end
  end})
  print(mytable.key1,mytable.key2)

输出结果为

value1	metatablevalue

分析mytable表赋值{key1 = "value1"}

mytable设置元表,元方法为__index

在mytable表中查找key1,如果找到,返回该元素,找不到继续

在mytable表中查找 key2,如果找到,返回 metatablevalue,找不到则继续。

判断元表有没有__index方法,如果__index方法是一个函数,则调用该函数。

元方法中查看是否传入 "key2" 键的参数(mytable.key2已设置),如果传入 "key2" 参数返回 "metatablevalue",否则返回 mytable 对应的键值。