Lua 基础

Lua

Lua 是一种由C编写的解释性脚本语言。编程

基础

  1. 语言的基础 变量、数据结构、流程控制、编程风格等。
  2. 语言的模型 模块化方式、内存管理。

变量

  1. lua 全部变量都是弱类型限制的。
  2. local 修饰则为局部变量,仅在当前块中有效,事后自动释放。
  3. local 修饰则为全局变量,直接进入_G _ENV 等全局数组中。
  • 类型
类型 示例 备注
nil x=nil nil 就是null
boolean x=false nil,false=false,true,非nil=true
number x=1 y=1.2 number自适应于整型、浮点型
string x='str' 字符串类型,无字符类型
function x=function()end 函数亦是基本的变量类型
coroutine 协程类型
userdata C/C++ 数据类型
table x={} lua 核心数据结构
  • 基本方式
-------------------------------------
-- 两种方式定义函数,效果同样
local function def()
    -- 栈式返回值
    return true,'hello world'
end

local def = function()
    return true,'hello world'
end

-- 栈式赋值
local a,b = def()

-- 二元赋值
local a = 0 or 1     -- 此时 a 为 0
local a = nil or 1   -- 此时 a 为 1

-- 三元赋值
local a = true and 1 or 0   -- a 为 1
local a = false and 1 or 0  -- a 为 0

-- 最多见的方法
a = tostring(123456)
a = tonumber("123456")
a = type(a)
  • string
-- base定义
str = 'hello world'
-- 经常使用定义,内部可携带:单引号且不用转义
str = "hello'-'world"
-- 多行字符串
str = [[
    hello world!
]]

-- 字符串长度
print(string.len("str"),#str)
-- 字符串链接
print('hello '..'world')

-- 大小写转化
string.upper(a)
string.lower(a)

-- char ==> string
string.char()
-- string ==> char
string.byte()

-- 截取字串
string.sub(str,[start],[end])
-- 查找字串位置
string.find(str,sub_str,[start],[end])

-- % 占位符格式化字符串,%d数字 %s 字符串 %% %
string.format("hello %s %d","word",123)
  • array
-- 数组定义方式
local x = {1,2,3,'4'}

-- 数组类型:table,实际并不是如此
print(type(x))
-- 数组、字符串的下标都从 1 开始
print(#x, #'hello', x[1])

-- 数组新增
table.insert(x,1,10)
-- 数组删除某项值
table.remove(x,1)

-- 链接大量字符串,消耗少,速度快
table.concat(x,"-")
  • 引用
  1. 标准引用都是强引用模式,通常对象亦可分为引用对象,已经非引用对象。
~ 传递 复制
基础类型 值传递 深复制
引用类型 引用传递 浅复制
local num = 100
local tmp = { a = 100, b = 120 }
-- 此时执行 值传递
dat = num, dat = tmp.a
-- 此时执行 引用传递
local test = tmp


-- 弱引用
local tmp = {}

-- key,弱引用
setmetatable(tmp, { _mode = "k" })
-- value,弱引用
setmetatable(tmp, { _mode = "v" })
-- key-value, 弱引用
setmetatable(tmp, { _mode = "kv" })

table

  • 说明
  1. lua数组、表并不同,须要注意。
  2. 迭代器,可迭代全部table类型。
  3. table数组以及表,内存是一致的,表须要遍历,数组可直接定位。
~ pairs ipairs
迭代时遇到nil 跳过本次 中止迭代
x = {1,nil,3,4}

-- 1
for _,v in pairs(x) do
    print(v)
end
-- 1 3 4
for _,v in ipairs(x) do
    print(v)
end
  • 映射表
-- 基本定义方式
local p = { a=10, b=20 }

-- 索引命中方式
print(p["a"], p.a)
-- 新增索引项
p.c = 100

-- 字符串索引,任意合法的字符串均可以做为键
p[" "] = 123456

-- 映射:将内存数值映为可读字段
local state = { login = 1, logoff = 0 }
  • 元表
  1. index 元表,本质是添加一个附表。
  2. newindex 元表,则是在表添加新索引时执行的操做。
-- index 元表
local tmp = { x=1, y=2 }
setmetatable( tmp, { __index = {
    z = 10
}})
-- 另外一种方式
local tmp = setmetatable({x=1,y=2},{__index={z=10}})

-- 索引为nil或不在原始表中,则会在附表中查找
print(tmp.x, tmp.y)

-- newindex
local tmp = { x=1, y=2 }
setmetetable( tmp, { __newindex=function(tab,key,val)
                                    -- 此处可不执行,用以锁定 table
                                    rawset(tab,key,'val:'..str(val))
                                end
                    })
-- 新增索引,执行newindex方法
-- y = 'val:100'
tmp.y = 100
-- 此时y已存在,不执行newindex
-- y = 100
tmp.y = 100

-- rawget 获取表中 key(字符串) value
local dat = rawget(tab,"key")
-- 设置表 tab,key(字符串) val
rawset(tab,key,val)
  • function
  1. 匿名函数,异步回调。
  2. 闭包函数,须要注意可能形成内存泄漏。
-- 异步回调
local function demo(args, fun)
    print("function demo is running")
    fun(args)
end
-- 将匿名函数作为参数传递至原方法内部
demo("hello world", function(args)
    print(args)
end)

-- 闭包函数
local function demo(args)
    -- 闭包函数返回后, args参数不会被释放
    return function()
        print(args)
    end
end

demo("hello world")

控制

  • 运算符
1. 算术运算
+ - * / // % ^  -- / 除法运算 // 整除运算

2. 关系运算
== ~= > >= < <=

3. 逻辑运算
and or not

4. others
. [] () {} , : # .. =
  • 条件控制
-- 顺序判断
if true then
    print(true)
elseif true then
    print(true)
else
    print(false)
end

-- 经常使用判断
if true then
    print(true)
end
  • 循环控制
--------------------------------------------
-- lua没有 `switch` 选择循环
-- lua没有` continue`退出,仅存在 `break`退出块。

-- while循环
while(true) do
    print(true)
    break
end
-- repeat循环,true为退出条件
repeat
    print(false)
until false
-------------------------------------------
-- for 头部默认为内部块变量,执行完毕自动释放

-- start stop step
for i = 0,10,1 do
   print(i)
end
-- k为索引,v为值,同时适配与表格和数组
for k,v in pairs(tab) do
    print(k,'--',v)
end
--------------------------------------------
--  for 头部亦可引用外部变量

local i
for i = 0,10,1 do
   print(i)
end
local k,v
for k,v in pairs(tab) do
    print(k,'--',v)
end
--------------------------------------------
-- _ 虚变量引用
for _,v in pairs(tab) do
    print(v)
end
  • 异常控制
-- 若此处为false,程序中断并抛异常
assert(1 == 1)

-- 以保护模式执行某方法,程序不会中断
local ok,err = pcall(demo())

local function demo(x,y)
    print(x/y)
end

-- 以保护模式执行 demo,参数在其后,成功返回true,失败返回 false,err
local ok, err = pcall(demo, 10,5)

print(ok,err)

local function print_err()
    print(debug.traceback())
end

-- 执行 demo,出错执行 print_end,参数在其后
xpcall(demo,print_err(),0,10)

模块

面向对象

lua 面向对象是一种伪对象,实际是一个衍生的索引表。数组

  • 模块,类,对象
  • 模块:块私有变量或方法仅模块内部生效,外部没法使用
  • 类: class仅可读写全部 .级的方法以及变量。
  • 对象:obj仅可读写全部 self级的变量以及方法,仅可读全部 . 级变量以及方法,强写class级变量实际是新建一个obj级变量,对整个class并不生效。
  • 读写分离

模块,类,对象之间的读写应该分离以避免出现系统性error,全部超出限度的强写实际都是新建一个当前等级的变量,原始上一级变量并未发生改变。数据结构

模块 对象
模块 读写
读写
对象 读写
-- class
--------------------------------------------
local class = {}

-- 模块私有方法,仅仅块内生效
local function example()
    print("example")
end

-- 能够看做类变量
class.size = 100

-- 能够看做类方法
function class.print_size()
    print(class.size)
end

-- 类new对象的方法
function class:new(x,y)
    cla = {}
    -- 对象变量
    cla.x = x or 1
    cla.y = y or 2
    setmetatable(cla,{__index=self})
    return cla
end

-- 对象方法
function class:print()
    print(self.x,self.y)
end

return class

-------------------------------------------------
-- obj

-- 引用块
local class = require("example")

-- class访问
class.print_size()

-- obj 访问
local obj = class:new(10,20)

-- obj -> class 访问
obj.print_size()

面向切面

面向切面编程是一种特殊的设计方式,面向对象的主要目的是为了代码复用,面向切面则是一种函数结构化方法。闭包

-- 状态码
local STATE = {
    PREPARE = 0,
    FIGHT   = 1,
    REWARD  = 2,
    REFRESH = 3,
}
-- 主方法
local function running = {}
running[STATE.PREPARE] = function(comp)
    -- pass
end
running[STATE.FIGHT]   = function(comp)
    -- pass
end
running[STATE.REWARD]  = function(comp)
    -- pass
end
running[STATE.REFRESH] = function(comp)
    -- pass
end

-- running 主程序
while(true) do
    running[comp.state](comp)
end

高级特性

  • 加载模块
  1. lua 全局变量存储在:解释器级变量_G _ENV中。
  2. lua package.loaded:存储加载完成的模块。
  3. lua package.path:require 适配路径。
-- lua 模块加载的基本路径
local path = package.path

-- lua 检测模块是否已加载
if package.loaded["exp"] then
    print("exp 模块已加载!")
end

-- lua 热更新指定模块
local function hotfix(file)
    if package.loaded[file] then
        package.loaded[file] = nil
    end
    require(file)
end
--------------------------------------------
local x = require("x")
-- 先扫描 require("x")路径
-- 查找须要被加载的路径是否已经被加载
-- 若模块未被加载,则加载模块,并返回索引
-- 若模块已被加载,则直接返回索引

-- 加载某路径下的文件,并执行
-- 执行语句则,加载编译,执行
dofile("luafile.lua")
-- 加载指定文件,封装为一个函数返回
-- 索引仅需加载一次
local load = loadfile("luafile.lua")

--------------------------------------------
-- 弱类型,编译解释
-- 全部变量,在第一次被加载的时候,会被肯定下来
-- 加载时,会将代码编译为机器码,而后给解释器
-- 之后执行时,皆使用加载时的配置类型
local y = 100   -- 编译时肯定为 int
local s = 'he'  -- 编译时肯定为 string
  • 预加载机制
  1. 预加载模块时,变量与方法会进入内存。
  2. 预加载模块时,语句块仅会执行一次,并不会进入内存。
  3. 全部lua文件,在虚拟机启动时即需完成预加载,除非后续热更新。
local exp = require("example")
local ret = {}

-- 被预加载后,进入内存,但仅可局部使用
local inner = "inner value"
local function inner_fun()
    print("hello world\n")
end

-- 被预加载后,进入内存,外部亦可以使用
ret.outer = "outer value"
function ret.out_fun()
    print("hello world\n")
end

-- 预加载时
-- function 内的语句块会进入内存
-- function 外的语句块会执行一次,但不进入内存
print(inner)
inner_fun()

-- return 模块
-- 语句块会被执行一次,而后丢弃
-- 全部成员所有会被加载进入内存
-- 若成员被local修饰则非本模块对象没法访问
return ret
  • 内存管理
  1. lua GC由解释器隐士处理,亦可自行显示处理。
  2. lua local变量会自动释放,var=nil亦可释放内存。
  3. _G _ENV 这两个内部存储全局变量。
  4. collectgarbage() GC管理。
  5. stop the world,标记清除。
local x = 100
local y = { a=100, b=200 }
z = 100
-- 不推荐使用全局变量
if _G["z"] then
    print("global var z"..z)
end
-- 释放全局变量
y = nil
-- 释放局部变量
x = nil
-- 释放table内变量
y.a = nil

-- 执行一次 GC
collectbardage("collect")
-- 计算当前程序所占总内存
local num = collectgarbage("count");

-- 慎用,解释器中止自动GC,必须手动处理,不然内存会爆炸
collectbardage("stop")

经常使用

记录一些经常使用的,易遗忘的方法以及小技巧。异步

经常使用模块

  • 文件管理
local file = io.open(".../example.txt","w")

-- 读写外加参数便可
local str = file:read()
file:write("hellow world")

-- 关闭文件
io.close(file)
file:close()

lua 协程仍是单线程工做,只是同一时间里只有一个协程在工做,且是守护线程。
拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。性能

-- 线程总得是无限循环的方法
local function done(dat)
    while()
        print(dat)
    end

    -- 协程执行至此处则 dead
end

-- 建立协程 suspended 挂起状态
local cor = coroutine.create(done)

-- 获取指定协程的现行状态 dead,suspended,running
print(coroutine.status(cor))

-- 重启或开启协程,协程引用,外加参数(没有能够不加) running
coroutine.resume(cor,"hellow world")

-- 挂起当前正在运行的协程,并使得协程返回数据 dat,协程最多只有一个能够运行
coroutine.yield(dat)

-- 返回某个正在running 的协程的线程ID
coroutine.running()

生产者消费者模型测试

1.coroutine.resume(cor) :开启协程等待返回值 status,valueui

2.coroutine.yield(dat) :挂起运行协程返回 dat

-- 弱类型语言的好处
local produce

-- 暂停程序 1 秒,获取信息
local function step()
    os.execute("sleep 1")
    print(coroutine.running())
    print(coroutine.status(produce))
end

-- 生产者模型
local function productor()
    local i = 0
    while true do
         step()
         i = i + 1
         -- 挂起当前协程,并返回 i
         coroutine.yield(i)
    end
end

-- 消费者模型
local function consumer()
    while true do
        local status,value = coroutine.resume(produce)
        step()
        print(status,"--",value)
    end
end

produce = coroutine.create(productor)

consumer()

经常使用方法

不须要解释,容易忘记的东西。

-- 执行系统cmd
os.execute("sleep 1")

-- 输出 格式化日期 2020-01-01 01:02:56
print(os.date("%Y-%m-%d %H:%M:%S"))

-- 输出当前时间戳
print(os.time())

-- 输出程序已经执行的时间
print(os.clock())

-- 将表转化为字符串
local function tostr(tab)
    local str =  "{ "
    str = str.."\n"
    for k, v in pairs(tab) do
       if type(v) ~= "table" then
           str = str.."[\""..k.."\"]"
           str = str..":"
           str = str..v
           str = str..","
           str = str.."\n"
       else
           str = str.."[\""..k.."\"]"
           str = str..":"
           str = str..tostr(v)
           str = str..","
           str = str.."\n"
       end
    end
    str = string.sub(str, 1, -3)
    str = str.."\n"
    str = str .." }"
    return str
end

经常使用设计

一些通用性的程序设计。

  • 平滑
-- 模块尽可能使用,if return
if true then
    return "err"
end

-- 方法不变,选择不一样的参数执行
local function execute(param)
    print(param)
end

-- 参数不变,选择不一样的方法执行
local Execute = {}
Execute[CODE] = function(param)
    print(param)
end
  • 容错
 
  • 幂等
-- 程序设计时,根据实际的

进阶

  • 性能测试
-- 执行所用CPU时长
local begin     = os.clock()
-- 获取CPU时间戳
local beginTime = os.time()
print("hello world")
local over     = os.clock()
local overTime = os.time()

-- 0.001212  0.001234  number
print(begin, over, type(over))
-- 1596786393  1596786393  number
print(beginTime, overTime, type(overTime))
  • 遍历加速
  1. iparis 仅用于迭代数组类型table,遇到nil会退出。
  2. pairs 则可用于全部类型table,遇到nil则会跳过,当迭表明时是乱序的。
local tab = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }

-- 0.038 ms, 此处 #tab 会一直执行,故必然拖慢速度
for i = 1, #tab do
    print(tab[i])
end

-- 0.030 ms
local len = #tab
for i = 1, len do
    print(tab[i])
end
-- 0.035 ms
for _,val in pairs(tab) do
    print(val)
end
-- 0.043 ms
for _,val in ipairs(tab) do
    print(val)
end
-- 遍历基础数组,速度并没有太多差别

local tab = {
    a = 1, b = 2, c = 3, d = 4, e = 5,
    f = 6, g = 7, h = 8, i = 9, j = 10
}

-- paris 迭代 数组、表格
-- pairs 迭表明格时,顺序为随机序列
for k, v pairs(tab) do
    print(k, v)
end
  • 传值与引用
  1. 全部的基础类型对象都执行深复制。
  2. 全部的引用类型对象都执行浅复制。
相关文章
相关标签/搜索