Lua简单学习

前言

之前看过一段时间的xLua,使用xLua和Unity交互,但是对lua本身不怎么了解,于是初步学习下lua。

lua

local 局部变量 不声明默认全局变量

函数:

可以作为参数传来传去,类似C#的委托。

1
2
3
4
5
6
7
8
9
10
11
myprint = function(param)
print("这是打印函数 - ##",param,"##")
end
function add(num1,num2,functionPrint)
result = num1 + num2
-- 调用传递的函数参数
functionPrint(result)
end
myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)

递归函数

  • local f, g 需要标记为局部变量
    1
    2
    3
    4
    5
    6
    function g ()
    … f() …
    end
    function f ()
    … g() …
    end

多返回值

1
2
3
4
--string.find 返回匹配串"runoob" 在"www.runoob.com"中的"开始和结束的下标" lua中的索引从1开始
s, e = string.find("www.runoob.com", "runoob")
print(s, e)
5 10

传参

1
2
3
4
5
6
7
8
9
10
11
12
13
--参数不存在什么类型,直接把列表传入
function maximum (a)
local mi = 1 -- 最大值索引
local m = a[mi] -- 最大值
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
print(maximum({8,10,23,12,5}))

…表示可变参数,传一个两个三个随你,相当于C#的params关键字

1
2
3
4
5
6
7
8
9
10
function average(...)
result = 0
local arg={...}
for i,v in ipairs(arg) do
result = result + v
end
print("总共传入 " .. #arg .. " 个数")
return result/#arg
end
print("平均值为",average(10,5,3,4,5,6))

迭代器

无状态迭代器

  • 迭代过程中直接在迭代器中计算,返回结果
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    --迭代器
    function square(iteratorMaxCount,currentNumber)
    if currentNumber<iteratorMaxCount
    then
    currentNumber = currentNumber+1
    return currentNumber, currentNumber*currentNumber
    end
    end
    --开始迭代
    for i,n in square,3,0
    do
    print(i,n)
    end
  • 多状态迭代器

  • 利用闭包,把迭代状态作为一个闭包函数传递出来,简洁干净,(或者直接利用table把所有可能用到的信息存储起来。)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    array = {"Lua", "Tutorial"}
    --迭代器
    function elementIterator (collection)
    local index = 0
    local count = #collection
    -- 闭包函数
    return function ()
    index = index + 1
    if index <= count
    then
    -- 返回迭代器的当前元素
    return collection[index]
    end
    end
    end
    --使用返回的函数进行迭代
    for element in elementIterator(array)
    do
    print(element)
    end

模块

类似封装库.

定义一个Module

1
2
3
4
5
6
7
8
9
10
11
12
module = {}
module.constant = "这是一个常量"
function module.func1()
print("这是一个公有函数")
end
local function func2()
print("这是一个私有函数")
end
function module.func3()
func2()
end
return module

加载Module

1
2
3
4
5
require("module")
print(module.constant) --这是一个常量
module.func3() --这是一个私有函数
或者定义一个local变量,方便调用
local m = require("module")

常规数据结构

一维数组

1
2
3
4
5
6
7
8
9
--一维数组
a = {}
for i = 1,100 do
a[i] = 0
end
print("The length of array 'a' is " .. #a)
squares = {1, 4, 9, 16, 25}
print("The length of array 'a' is " .. #squares)

二维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--二维数组
local N = 3
local M = 3
mt = {} --表结构实现
for i = 1,N do
mt[i] = {} --mt[i] 对应一个新表,表中嵌表
for j = 1,M do
mt[i][j] = i * j
end
end
--这类实现已经不能称之为常规的二维数组了,二维索引值经过一个函数转化成最终的key
mt = {}
for i = 1, N do
for j = 1, M do
mt[(i - 1) * M + j] = i * j
end
end

链表

简单实现单向链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 链表
list = nil
for i = 1, 3 do
list =
{
next = list , --把先前的整条链塞到next,第一个节点自然是没有next的
value = i
}
end
local l = list
--在内存中的存储形式是这样的list= { next= { next= { value=1} ,value=2} ,value=3}
while l do
print(l.value)
l = l.next
end

双向链表,增删改查封装下即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
--定义list
list = {
first =nil,
last = nil,
}
--构造节点
function list:new()
local t = {pre =nil,suc = nil,value = 0}
return t
end
--添加节点
for i = 1, 3 do
local node =list:new()
node.value = i
if list.first == nil
then
list.first = node
elseif list.last == nil
then
list.last = node
list.first.suc = list.last
list.last.pre = list.first
else
list.last.suc = node
node.pre = list.last
list.last = node
end
end
--循环遍历链表
local temp = list.curnode
while temp do
print(temp.value)
temp = temp.pre
end

词典

table本身就是散列表结构

队列和栈

在数组的基础上稍作修饰即可

元表:

  • Lua中的每一个表都有其Metatable。Lua默认创建一个不带metatable的新表
  • oop中的类,可以通过元表模拟出来,class和function
  • 继承可以用__index模拟出来
  • 可以使用setmetatable函数设置或者改变一个表的metatable
    1
    2
    3
    4
    5
    mytable = {} -- 普通表
    mymetatable = {} -- 元表
    mymetatable2 = {}
    setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
    setmetatable(mytable,mymetatable2) -- 把 mymetatable2 修改为 mytable 的元表

获取元表

1
getmetatable(mytable) -- 返回mymetatable

__index 元方法

  • 首先这是一个键,他是元表的键,不是table的键,__index对应的是table,即一个表的元表。

  • 一个table只能对应一个元表。当使用这个键来赋值table时,如果这个键已经被赋值,此时如果再进行赋值,就会覆盖之前的值。

  • 当使用这个键来访问table时,如果table中不存在对应的key,则会在元表中试图递归的查找。

  • 如果不是通过index键来设置的表,表内的值则无法通过index来访问到,这点值得注意。

  • 给表添加字段,字段是加在表中,而不是加在元表中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    oneTable = {name = "lyu"}
    otherTable = {name1 = "lyuan"}
    otherTable2 = {name2 = "lyua"}
    otherTable3 = {name3 = "lyuan"}
    t=setmetatable(oneTable,{__index = otherTable})
    t.newkey = "add key"
    print(t.newkey.."|", oneTable.newkey.."|", otherTable.newkey) -- 新的字段加在表上 add key| add key| nil
    t=setmetatable(oneTable,{__index = otherTable2}) --重新设置oneTable的元表
    print("log:",t.name1,t.name2) -- log: nil lyua
    setmetatable(otherTable2,{__index = otherTable}) --设置otherTable2的元表
    print("log:",t.name1,t.name2) -- log: lyuan lyua nil
    t = setmetatable(oneTable,otherTable3) --一个table只能设置相应的一个元表
    print("log:",t.name1,t.name2,t.name3) -- log: nil nil nil 如果不是通过__index键来设置的表,表内的值则无法访问到

如果index对应的是一个函数,当试图通过index访问某个元表数据时,lua就会调用这个函数,table和键作为参数传递个这个函数。

1
2
3
4
5
6
7
8
9
10
11
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

元表是对普通表的扩充

总结:

  • 1.在表中查找,如果找到,返回该元素,找不到则进入下一步。
  • 2.判断该表是否有元表,如果没有元表,返回nil,有元表则进入下一步。
  • 3.判断元表有没有index方法,如果index方法为nil,则返回nil;如果index方法是一个表,则重复1、2、3;如果index方法是一个函数,则返回该函数的返回值。

__newindex 元方法

index则用来对表访问,相应的newindex 元方法用来对表更新。

__newindex 只能访问到已存在的key-value,新增的无法访问到。

通过__newindex设置的空表,新的字段是加在空表上,也就是元表上,如果元表不是空表,字段就是加在表上

由于不是__index访问器,所以空表的元表是不能访问到的。

1
2
3
4
5
6
7
8
9
10
blanktable = {}
t=setmetatable({key1 = "key1value"},{__newindex = blanktable})
t.newkey = "//newvalue"
print("log:",t.newkey,blanktable.newkey) -- log: nil //newvalue
t.key1 = "//key1 value update"
print("log:",t.key1,blanktable.key1) -- log: //key1 value update nil
t.key2 = "//add a key2"
t.key3 = "//add a key3"
print("log:",t.key2,blanktable.key2) -- log: nil //add a key
print("log:",t.key2,t.key3,blanktable.key2,blanktable.key3) -- log: nil nil //add a key2 //add a key3

相应的操作非空表时,新的字段在origintable中增加,而不是在元表中增加。

1
2
3
4
5
6
7
blanktable = {key2 = "key2value"}
origintable = {key1 = "key1value"}
t=setmetatable(origintable, blanktable)
t.key2 = "newvalue"
print("log:",t.key2,blanktable.key2) -- log: newvalue key2value
t.key3 = "key1 value update"
print("log:",t.key3,origintable.key3,blanktable.key3) -- log: key1 value update key1 value update nil

如果newindex对应的是一个函数,和index类似

1
2
3
4
5
6
7
8
9
mytable = setmetatable({key1 = "value1"}, {
__newindex = function(mytable, key, value)
rawset(mytable, key, "\""..value.."\"")
end
})
mytable.key1 = "new value"
mytable.key2 = 4
mytable.key3 = 5
print(mytable.key1,mytable.key2,mytable.key3) -- new value "4" "5"

为表添加操作符

__add 对应的就是”+”,这里的”+”的运算规则交由我们自己来定义,相当于重写了运算符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
__add = function(mytable, newtable)
for i = 1, table_maxn(newtable) do
table.insert(mytable, table_maxn(mytable)+1,newtable[i])
end
return mytable
end
})
-- 第二个表
secondtable = {4,5,6}
-- 相加
mytable = mytable + secondtable
for k,v in ipairs(mytable) do
print(k,v)
end
----------------------输出
[LUA-print]: 1 1
[LUA-print]: 2 2
[LUA-print]: 3 3
[LUA-print]: 4 4
[LUA-print]: 5 5
[LUA-print]: 6 6

__add 对应的运算符 ‘+’.
__sub 对应的运算符 ‘-‘.
__mul 对应的运算符 ‘*’.
__div 对应的运算符 ‘/‘.
__mod 对应的运算符 ‘%’.
__unm 对应的运算符 ‘-‘,取反符号
__concat 对应的运算符 ‘..’.连接符
__eq 对应的运算符 ‘==’.
__lt 对应的运算符 ‘<’.
__le 对应的运算符 ‘<=’.

__call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
-- 定义元方法__call
mytable = setmetatable({10}, {
__call = function(mytable, newtable)
sum = 0
for i = 1, table_maxn(mytable) do
sum = sum + mytable[i]
end
for i = 1, table_maxn(newtable) do
sum = sum + newtable[i]
end
return sum
end
})
newtable = {10,20,30}
print(mytable(newtable))

__tostring元方法

就像oop语言中的重写ToString()方法。

1
2
3
4
5
6
7
8
9
10
mytable = setmetatable({ 10, 20, 30 }, {
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "表所有元素的和为 " .. sum
end
})
print(mytable) --表所有元素的和为 60

__metatable

通过设置__metatable = “Private”,可以使元表不能被修改,以只读的形式存在

总结

在lua中可以把table看作一个类,元表中设置call方法,达到相当于类中定义方法,index中可以访问到继承的子类,newindex可以通过赋值空表来完成对一个元表的字段增加,tostring没什么可说,各类运算符通过重写可以快速的进行类之间的操作。