C/C++程序员的Lua速成

120 阅读5分钟

一. 一些小的注意点

  • 条件判断
num = 0
if num then print("true")
else print("false")
end
-- 输出结果为:true
  • 只有在num = nilnum = false时,才会打印false

  • **num = 0时也为true **

  • 不等于

if num ~= 10 then print(num) --和C的不等于写法不一样
end
  • 三目运算
r = a and b or c -->这样的写法存在一个BUG,当b=nil时, 返回c
r= (a and {b} or {c})[1] -->这样才是安全的写法
  • for
for i=0,10 do --i范围[0,10]
    print(i)
end
for i=10,0,-1 --i范围[10,0]
    print(i)
end
  • 闭包&匿名函数
function adder(x)
    return function (y) return x + y end
end
a1 = adder(9)
a2 = adder(36)
print(a1(16)) --25
print(a2(64)) --100
--这些也是等价的:
local function g(x) return math.sin(x) end
local g
g = function (x) return math.sin(x) end
  • 多返回值, 没用到的会被丢掉
x, y, z = 1, 2, 3, 4 --x=1,y=2,z=3,4被丢掉
function bar(a, b, c)
    print(a, b, c)
    return 4, 8, 15, 16, 23, 42
end
x, y = bar('zaphod')  --print "zaphod  nil nil"
-- 现在 x = 4, y = 8, 而值15..42被丢弃
  • 变长参数
function add(...)
	local s = 0
	for i,v in ipairs{...} do
		s = s + v
	end
	return s
end
print(add(3,4,10,25,12)) -- 54
  • 字符串与数字
print("10" + 1) -- 11, 会自动转为数字相加
print("hello" + 1) -- 错误
print(11 ..22) -- 1122, 注意,数字后面要跟空格再跟".", 否则会认为是小数点
  • 全局变量_G

所有的全局变量都是放在__G表里

for v in pairs(_G) do print(v) end
val = 10
print(_G['val']) -- 10

二. 基础数据类型

共8种:

  1. nil
  2. bool
  3. number
  4. string
  5. table
  6. userdata
  7. function
  8. thread

三. table

lua的唯一数据结构, 非顺序存储

  • 语法格式
t={
    [2]="v1",
    [3]=6,
    'v2', 
    idx3='v3', 
    ['hello']=5, 
    "v4"
    }
print(t[1]) -- v2
print(t[2]) -- v4
print(t[3]) -- 6
print(t.idx3) -- v3
print(t["idx3"]) -- v3
print(t['hello']) -- 5
print(t.hello) -- 5

用中括号括起来,是索引下标 不用中括号,默认是字符串索引

3.1 基础

  • "#"号,求table长度, 下标必须从1开始, 到第一个自增不为1时结束
tbl = {[1] = 1, [2] = 2, [3] = 3}
print(#(tbl)) -- 3
tbl = {[1] = 1, [2] = 2, [6] = 6}  
print(#(tbl)) -- 2
tbl = {['a'] = 1, [2] = 2, [3] = 6}  
print(#(tbl)) -- 0
  • table.maxn(), 获取整数下标的最大值. 无法获取字符串下标
tbl = {[1] = 1, [2] = 2, [3] = 3}
print(table.maxn(tbl)) -- 3
tbl = {[6] = 6, [1] = 1, [2] = 2}
print(table.maxn(tbl)) -- 6
tbl = {["a"] = 1, ["b"] = 2, ["c"] = 3}
print(table.maxn(tbl)) -- 0
print(#(tbl)) -- 0

3.2 table遍历

for key, value in pairs(tbl) do  
    XXX  
end
-- ipairs必须是table下标从1开始,而且每次自增1, 否则遍历会结束
for key, value in ipairs(tbl) do  
    XXX  
end 
for i=1, #(tbl) do  
    XXX  
end
for i=1, table.maxn(tbl) do  
    XXX  
end 

四. 类

lua是函数式编程, 没有类. 我们的类是一个表,它定义了各种属性和方法。 我们的实例也是一个表 然后我们类作为一个元表设置到实例上,并设置类的__index值为自身

4.1 metatable和metamethod

运算符metamethod描述
+__add(a,b)
-__sub
*__mul
/__div
%__mod取模
__powN次方
==__eq等于
<=__le小于等于
<__lt小于
~=, >, >=a~=b表示为not(a==b),a>b表示为b<a
__concat字符串连接
__tostring(tbl)转字符串
__index(tbl,key)当访问table不存在的域时会调用
__newindex(tbl,key,val)对table中不存在的域赋值时调用

4.2 语法糖

Person = {name="这个人很懒"}
-- 正常写法
Person.talk = function(self, words)
print(self.name.."说:"..words)
end
--下面写法与上面等价
function Person.talk(self, words)
print(self.name.."说:"..words)
end
--下面写法与上面等价
function Person:talk(words)
print(self.name.."说:"..words)
end
--下面两句等价
Person:talk("你好")
Person.talk(Person, "你好")

####4.3 类实现

  • 一个简单的类
Class = {}
Class.__index = Class
--构造函数
function Class:new(x,y)
    local temp = {}
    setmetatable(temp, Class)
    temp.x = x
    temp.y = y
    return temp
end
function Class:test()
    print(self.x,self.y)
end
 -- 点调用,需要传递this指针
object = Class.new(object,10,20)
object:test() -- 10 20
--下面的冒号调用与上面点调用等效
object = Class:new(10,20)
object:test() -- 10 20
  • 继承

其实就是表与与的关联, 一个表找不到相应字段, 会去关联的表中查找

A = {}
function A:new(o)
	o = {}
	setmetatable(o,self)
	self.__index = self
	return o
end
function A:funName()
	print('A')
end
--B继承A
B = A:new()
s = B:new()
function B:funName()
	print('B')
end
s.funName() -- B
  • 私有成员

使用闭包函数, 将成员存储在闭包的upvalue中 创建对象, 就是返回一堆闭包的集合

function new(name, age)
	local inner = {mName = name, mAge = age}
	local _setName = function (name)
			inner.mName = name
		end
	local _setAge = function (age)
			inner.mAge = age
		end
	local _getName = function () return inner.mName end
	local _getAge = function () return inner.mAge end
	return {setName = _setName, setAge = _setAge, getName = _getName, getAge = _getAge}
end
----
obj = new("张三", 25)
print(obj.getName()) --> 张三
print(obj.getAge()) --> 25
----
obj.setName("李四")
obj.setAge(18)
print(obj.getName()) --> 李四
print(obj.getAge()) --> 18

五. 闭包

即内嵌函数

六. 协同

与线程的区别点

  • 任意时刻只能运行一个协同程序
  • 正在运行的协同程序, 只有显示suspend时, 他的执行才会暂停
  • 简单例子
co = coroutine.create(function () print("hi") end)
print(co) -- thread: 0x8071d98
print(coroutine.status(co)) -- suspended,协同被创建时, 默认是挂起的
coroutine.resume(co) -- hi
print(coroutine.status(co)) -- dead
  • 挂起(yield)
co = coroutine.create(function ()
	for i=1,5 do
		print("co", i)
		coroutine.yield()
	end
end)
coroutine.resume(co) --> co 1
coroutine.resume(co) --> co 2
coroutine.resume(co) --> co 3
coroutine.resume(co) --> co 4
coroutine.resume(co) --> co 5
coroutine.resume(co) --> 什么都不打印

当协同A唤醒协同B后, 协同A此时状态为'正常'态, B为'运行'态

  • 传递参数
co = coroutine.create(function (a,b,c)
	print("co", a, b, c)
end)
coroutine.resume(co, 1, 2, 3) --> co 1 2 3
  • 生产者-消费者
function receive()
	local status,value = coroutine.resume(producer)
	return value
end
function send(x)
	coroutine.yield(x)
end
producer = coroutine.create(function ()
	while true do
		local x = io.read()
		send(x)
	end
end)
consumer = coroutine.create(function ()
	while true do
		local x = receive()
		io.write(x, "\n")
	end
end)
coroutine.resume(consumer)

七. 内部实现

7.1 Lua中的值(TValue)

Lua中的值类型.png

  • tt : 用于标识类型
  • p : 存储的light userdata
  • gc : 需要回收的值

7.2 字符串(TString)

  • 字符串, 一旦创建, 就无法修改, 不使用时会被垃圾回收(GC)
  • 分为长字符串和短字符串, 界线由宏LUAI_MAXSHORTLEN决定,默认为40
  • 所有短字符串, 均被放在global_State.strt中, strt是stringtable, 是一个哈希表
  • 相同的短字符串, 在同一个lua_State中, 只会存在一份
  • 长字符串则独立存放(TODO), 没有做唯一化

7.3 表(Table)

  • lua中唯一的数据结构
  • 由 数组部分 + 哈希部分 实现

Lua中table的数据结构

7.3 闭包

一个闭包, 是由一个函数指针, 加一个upvalue组成 upvalue中记录了所有外层函数变量

闭包结构体

  • 当外层函数没有返回时, 闭包的upvalue是直接指向数据栈上变量的位置. 所以这时候调用闭包函数访问外层变量, 其实就是直接访问外层的变量
  • 当外层函数返回时, 外层变量在出栈的时候, 若被upvalue引用, 则会将变量从数据栈复制到upvalue中