Lua学习笔记整理

Lua学习笔记整理

  • 一、基本语法
    • 标识符
    • 关键字(保留字)
    • 常量
    • 变量
    • 注释
  • 二、Lua数据类型
    • 数值类型
    • 布尔类型
    • 字符串类型
    • 自定义类型(userdata)
    • 函数类型
    • 线程类型
    • 表类型(table)
    • nil类型
  • 三、type函数
  • 四、类型转换
  • 五、运算符
  • 六、控制语句
    • 分支语句
    • 循环语句
      • while循环
      • repeat循环
      • for循环
      • 跳转语句
  • 七、表类型
    • 字典
    • 数组
    • for循环遍历table
      • ipairs
      • pairs
      • 典型for循环
  • 八、字符串
    • 字符串定义
    • 字符串拼接
    • 字符串截取
    • 字符串转换
    • 字符串查询
    • 字符串格式化
  • 九、函数
    • 函数定义
    • 多重返回值
    • 嵌套函数
    • 返回函数
    • 匿名函数
  • 十、面向对象
  • 十一、元表
    • __index元方法
    • __newindex元方法
    • 封装、继承、多态

一、基本语法

local function main()
	local msg = 'Hello Lua!'
	print(msg)
end
main()

标识符

lua中的字母均为ascii编码而非Unicode编码
标识符需要满足以下三点要求:

区分大小写;
标识符首字符可以为下划线(_)、美元符($)、或者字母开始;
标识符中其他字符可以是下划线(_)、美元符($)、字母或者数字组成

关键字(保留字)

语言中定义具有特殊含义的标识符,保留字不能作为标识符使用
包括:
and,break,do,else,elseif,end,false,for,function,if,in,local,nil,not,or,repeat,return,then,true,until,while

常量

Lua中常量和变量没有区别,通常在命名规范中要求大写常量标识符
eg:
NUM=100

变量

Lua 中的变量全是全局变量,无论语句块或是函数里,除非用 local 显式声明为局部变量,变量默认值均为nil
使用local关键字修饰的变量是局部变量,作用范围为变量声明所在的代码块(一个控制结构内、函数内、文件内或文件串内)

score = 0.0 -- 没有local关键字修饰,为全局变量
local score = 0.0 -- local关键字修饰,为局部变量
local score -- 变量未赋予初始值,默认值为nil,下同
score

注释

单行注释,使用 –
多行注释,使用 --[[ --]]

-- 单行注释
--[[
多行注释1
多行注释2
--]]

二、Lua数据类型

数值类型

包含整数和浮点数
整数:十进制和十六进制(以0x开始)
浮点数:必须包含一个数字、一个小数点或e(或E),如3.14159,-3.1E12,0.1e12

布尔类型

只有true(为真)和false(为假)两个值

字符串类型

若干个封装在双引号(")或单引号(’)内的字符,如"fish",“a line”

自定义类型(userdata)

在lua中可以通过自定义类型与C进行交互,通过自定义类型可以将任意的C语言数据存储在Lua变量中,代码交互

函数类型

在lua中函数可以作为一种数据类型使用,函数可以赋值给一个变量,也可以作为参数传递给其他函数

线程类型

表示一个线程,线程可以通过执行多个,每个线程拥有自己独自的栈、局部变量和指令指针

表类型(table)

表类型实现了一组关联数据类型,用{}表示

local point = {x = 10, y = 20}
print(point["x"]) -- 通过下标方式访问
print(point.y) -- 通过字典方式访问

表类型table内可以存放各种类型的变量(包括函数、表等),这也是lua实现面向对象的基础

nil类型

仅仅只有nil属于该类,表示无效值(在条件表达式中相当于false)

三、type函数

type函数可以返回变量或者数值的类型(得到的是字符串)

type("Hello Lua") -- string
type(100) -- number
type(100.0) -- number
type(true) -- boolean
type(print) -- function
type(nil) -- nil
type({x = 10, y = 20}) -- table
-- 比较类型时需要加上"",因为type得到的是字符串
if type(nil) == "nil" then
	-- do something
end

四、类型转换

tostring() 可以将bool类型和数值类型转换成字符串类型
tonumber() 可以将非数字的原始值转换成数字

local num = tonumber("AF", 16) -- 返回十六进制数字175
local num = tonumber("0xA") -- 返回10
local num = tonumber(true) -- 返回nil
-- tonumber函数有两个参数,第二个参数是基数(进制),默认基数为十进制

local tb = tonumber({x = 10, y = 20})
print(tostring(tb)) -- 输出nil

五、运算符

运算符 作用
+
-
*
/
% 取余
^
== 等于(可以比较不同类型)
~= 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于
and 逻辑与
or 逻辑或
not 逻辑非

算术运算符对整数和浮点数都有效

res = 17.6 % 7 -- 3.6

运算优先级(从上到下,由高到低)

优先级 运算符
1 ^
2 * /
3 + -
4 < > <= >= ~= ==
5 and
6 or

六、控制语句

分支语句

if 条件表达式 then
	语句组
end

if 条件表达式 then
	语句组1
elseif 条件表达式 then
	语句组2
.
.
.
else
	语句组n
end

循环语句

while循环

通常结构如下:
先判断后执行

[initialization]
while termination do -- termination为true则执行body
	body
	[iteration] -- 迭代语句,用于改变变量
end

repeat循环

通常结构如下:
先执行后判断

[initialization]
repeat
	body
	[iteration]
until termination -- 为true则终止循环体

for循环

通常结构如下

for var = exp1, exp2, exp3 do
	body
end
--[[
* var循环变量从exp1开始,到exp2的值为止,步长为exp3(exp3可省略,默认为1)
* 大于等于exp1,小于等于exp2,则执行循环体
--]]

也可以使用for-in结构

for i,v in ipairs(a) do -- a为数组,ipairs从a中取出循环变量i和元素v
	body
end

跳转语句

break语句 用于跳出循环结构
return语句 用于函数返回

return expression -- 返回参数
return -- 无参返回

七、表类型

字典

表类型使用{}创建和初始化,{}表示方式称为table的构造器(constructor)

Student1 = {id = "100", name = "Tony", age = 18}
Student2 = {["id"] = "100", ["name"] = "Tony",  ["age"] = 18}
print("Student1 ID:"..Student1["id"])
print("Student2 ID:"..Student2.id)

用两种创建和访问方式均可

数组

数组结构是一种特殊的table结构,同样用{}创建,在创建时没有键,只有值,键默认以数字从1开始排列

local studentList = {"张三", "李四", "王五"}
--[[
1:张三
2:李四
3:王五
--]]

for循环遍历table

ipairs

for i,v in ipairs(studentList) do -- ipairs只能用于数组,不能用于字典
	print(i..":"..v)
end

pairs

for k,v in pairs(studentList) do -- pairs从字典中取出键值对,对于数组同样适用
	print(k..":"..v)
end

典型for循环

for i=1, #studentList do -- #获取数组长度,同样典型for循环不能用于字典
	print(i..":"..studentList[i])
end

八、字符串

字符串定义

用双引号或单引号括起来表示字符串

lcoal s1 = "Hello 'Lua'!"
local s2 = 'Hello "Lua"!'

单引号内可以使用双引号,双引号内可以使用单引号

字符串拼接

使用两点表示字符串拼接

local s = "Hello".."Lua"

字符串截取

string.sub(s,n,m)函数,截取字符串s的第n到第m个字符之间的字符串

local s = "Hello Lua"
local sub1 = string.sub(s, 1, 3) -- "Hel"
local sub2 = string.sub(s, 2, -2) -- "ello Lu"

-1表示最后一个字符,-2表示倒数第二个字符…

字符串转换

string.lower(s) -- 小写
string.upper(s) -- 大写
string.rep("Hello", 3) -- 重复3次,得到HelloHelloHello
string.reverse("Hello") -- 翻转,olleH

字符串查询

string.find(s,sub) -- 从s字符串中查找sub字符串

local s = "Hello Lua"
local i,j = string.find(s, "Hello") -- 返回值有两个,i为开始位置,j为结束位置

字符串格式化

类似于C语言中printf的占位符
string.format(“format”, …)进行格式化输出
local s = string.format("%d",10) – s为"10"

九、函数

函数定义

function 函数名(参数列表)
	语句组
	[return 返回值]
end

多重返回值

Lua函数可以定义多重返回值
eg:

function func(a, b)
	return a + b, a - b
end
local c, d = func(1,2)

嵌套函数

Lua函数内还可以定义函数,默认情况下,嵌套函数的作用域在外函数体内(相当于默认local修饰)
eg:

function calculate(opr, a, b)
	function add(a,b)
		return a + b
	end
	function sub(a,b)
		return a - b
	end
	if opr == '+' then
		add(a,b)
	elseif opr == '-' then
		sub(a,b)
	end
end

返回函数

可以将函数作为另一个函数的返回类型使用

function func()
	function func2()
		-- do somethind
	end
	return func2()
end
local func3 = func()
func3()

匿名函数

函数可以不定义函数名

function func()
	returnfunc = function()
		-- do something
	end
	return returnfunc
end

十、面向对象

Lua中不能定义类,但能通过table类型实现面向对象的功能(将table类型变量当做对象)

Student = {id = 100, name = "Tony"}
function Student.toString()
	return "Name:"..Student.name.." id:"..Student.id
end
print(Student.toString())

这里的Student为一个对象,toString()函数只属于Student对象
Lua中可以使用self表示自身对象,并通过:调用函数

function Student:toString()
	return "Name:"..self.name.." id:"..self.id
end
print(Student:toString())

冒号(:)和点(.)的区别:

  • 通过冒号定义的方法会默认接受self参数;而通过点定义的方法不会
  • 通过冒号调用方法,会默认将对象自身(self)传递;而通过点调用则不会传递自身

*通过冒号定义的方法还是能通过点来进行调用的,同样,通过点定义的方法也是可以使用冒号来调用的。但是,要注意函数输入参数以及函数内是否使用了self(如果用点调用了由冒号定义的函数,且函数内使用了self,则self会为nil,从而出错)

类是对象的模板,通过Student对象作为原型,用来创建其他对象,则可以将Student原型称之为类了

function Student:create(o)
	o = o or {}
	setmetatable(o, self) -- 将self作为元表设置给o对象
	self.__index = self -- 表示首先在当前对象中查找操作,如果当前对象中没有相应操作,就会到原型Student中查找操作
	return o
end
local stu = Student:create({id=200, name = "Tom"}) -- 以Student为模板,创建了一个对象
print(stu:toString())

十一、元表

metatable是table中预定义的一系列操作
Lua中的每一个值都有或者可以有一个元表,但table和userdata可以拥有独立的元表,其他类型的值就只能共享其类型所属的元表
Lua在新建table时不会创建metatable,需要使用setmetatable来设置元表

print(getmetatable(a)) -- 查看一个变量的元表,nil表示没有元表

通过设置元表的元方法来进行对元表的设置,最常用的键为__index和__newindex

__index元方法

这是metatable最常用的键
当通过键访问table时,如果该键没有值,那么Lua就会寻找该table的metatable中的__index

  • 如果__index是table,Lua就在__index这个table中查找相应键
  • 如果__index是函数,则将table和键作为参数传入

eg:

Window = {}
Window.mt = {}
Window.prototype = {x = 0, y = 0, width = 100, height = 100}
Window.mt.__index = function(table, key)
	return Window.prototype[key]
end

function Window.new(t)
	setmetatable(t, Window.mt)
	return t
end

w = Window.new({x = 10, y =20})
print(w.x) -- 10,w中包含x变量,不读取Window中的值0
print(w.width) -- 100,w中不包含width变量,调用元表的__index(这里的__index为函数),调用函数后返回Window中的width的值100

__newindex元方法

当给一个table一个缺少的索引赋值,解释器就会查找__newindex元方法:如果存在则调用这个函数而不进行赋值操作
eg:

Window.mt.__newindex = function(table, key, value)
	print("update of element"..tostring(key)..tostring(value))
	rawset(table, key, value)
end

封装、继承、多态

利用metatable实现面向对象的三大特征:封装、继承、多态

  1. 封装(通过设置__index元方法)
People = {age = 18}
function People:new()
	local p = {}
	setmetatable(p, self)
	self.__index = self
	return p
end
function People:growUp()
	self.age = self.age + 1
	print("People's grow up: "..self.age)
end
  1. 继承
Man = People:new()
function Man:growUp()
	self.age = self.age + 1
	print("Man's grow up: "..self.age)
end
man = Man:new()
man:growUp() -- Man's grow up
  1. 多态
    Lua不支持函数多态,但是Lua是动态类型的语言,本身就可以支持指针多态
person = People:new()
person:growUp() -- People's grow up
person = Man:new()
person:growUp() -- Man's grow up

你可能感兴趣的:(Lua)