lua学习

lua学习

lua简介

设计目的

  • 为了嵌入到应用程序中,从而为应用程序提供灵活的扩展和定制功能

特性

  • 可扩展

    • 提供扩展接口和机制,通常扩展到c/C++中
  • 轻量级

    • 使用C语言编写,开源,编译后100k, 方便嵌入到别的程序中
  • 自动内存管理

  • 支持面向过程和函数式编程

应用场景

  • 游戏开发
  • 独立脚本
  • web应用脚本
  • 扩展和数据库插件:Mysql

环境安装

IDE:SciTe

  • 需要安装vc++2005sp1 ,去官网下载

vsCode

  • 调试环境复杂,git上好多做luadebug。稍后研究

基本语法

交互式编程

  • 直接在lua中编写,回车就执行

脚本时编程

  • 将lua程序代码保存为 .lua 格式 PS: hello.lua
  • 使用lua执行脚本 lua hello.lua lua
    windows F:\lua\hello.lua

注释

  • 单行–
  • 多行 --[[ 多行注释 --]]

标识符

  • 不能用_ABC,下划线加大写字母是lua的保留字

全局变量

  • 不需要声明
  • 给一个变量赋值就创建了这个变量
  • 访问没有初始化的变量不会出错,未初始化变量值为nil
  • 删除全局变量,将变量赋值为nil

数据类型

动态类型语言,像python,不要定义类型,直接定义变量并赋值

基本类型

  • nil 表示无效值 相当于false

  • boolean

    • false 和nil为假
    • true为真
  • number相当于double

  • string

    • 单引号或双引号都可以表示
    • 使用两个方括号,表示一块字符串 [[ 字符串块,可以有回车 ]]
    • 对数字字符串执行+ - ,编译器会隐式转换为number类
    • 使用#来计算字符串的长度,放在字符串的 前面,例如: len = “hello” print(#len)
  • userdata

    • 表示任意存储在变量中的C数据结构(通常是指针和struct)
  • table

    • 是一个关联数组(键值对key–value),索引可以是数字、字符串或者表类型。

    • 数组从1开始计数

    • table创建是通过”构造表达式“,{ }

      • 采用引用计数模型,当所有引用的变量都为nil,系统垃圾回收
      • 初始化表 myTable = { }
      • 指定值 myTable[1] = “lua”
      • 移除引用 myTable = nil
    • table操作

      • table.concat(table, [,seq [, start[,end]]]),将从start到end的所有元素列出(连接成新的字符串),使用分隔符seq隔开

        • fruits = {“banana”, “orange”, “apple”}
        • table.concat(fruits , " , ")
      • table.insert(table, [pos,] value);选择一个位置插入,默认为末尾

        • table.insert(fruits, 2, “graps”)
      • table.remove(table [,pos])

        • table.remove(fruits, 2)
      • table.sort( table[, comp])

        • table.sort(fruits)
  • function

    • 1 optional_function_scope function function_name(argument1, argument2, argument3…, argumentn)

      • optional_function_scope设置函数为全局还是局部,设置为局部加local关键字
    • 2 function_body

    • 3 return result_params_comma_separated

      • 函数返回值,lua可以有多个返回值,每个返回值以逗号隔开
    • 4 end

      • 以function开始,end结束
    • 特色:

      • 将函数作为参数传递给函数

        • myprint = function(param) print(“this is print #”, param, “#”) end
        • function add(num1, num2, funPrint) funprint(num1 + num2) end
        • add(2, 3, myprint)
      • 函数返回多个返回值

        • 1 --[[例如:string.find 其返回匹配串“开始和结束的下标”(如果不存在匹配串返回nil)]]
        • 2 s, e = string.find(“http://www.baidu.com”, “baidu”)
        • 3 print(s, e)
        • unpack 接受一个数组作为参数,并从下标1开始返回该数组的所有元素
      • 不定形参

        • 传入变长参数function add(…)
        • 将可变形参赋值给一个变量 local arg = {…} --arg为一个表,局部变量
        • 通过select("#", …) 可以获得可变参数的数量
        • 需要固定形参加上可变参数时, 固定形参放在前面
        • select(i, …) 用于访问i到最后的参数,一次读一个 arg = select(i, 。。。)
      • 第一类值函数

        • 函数可以存在变量

          • add( add(5, 7), 8)
        • 函数可以匿名传递 有点像lamban表达式

          • add = function(a, b) return a + b end
          • add(3, function(num1, num2) return num1> num2?num1; num2 )

实现switch

使用表,和匿名函数

local switch = {

	[1] = function()
		
	end,
	[2] = function()
		
	end,

}
local setUIFunc = switch[index]
if setUIFunc then
	setUIFunc()
end

end

变量

所有变量默认值都为nil

分类

  • 全局变量

    • 在函数中不加local,也是全局变量
  • 局部变量

    • 必须在变量名前面加上local,即使在函数中也是
    • 作用域为定义开始到语句块结束,和C语言中的局部变量一样
  • 表中的域

赋值

  • 主要用来交换变量,或者将函数调用那个返回给变量

  • lua可以对多个变量同时赋值

    • a, b = 10, 5
  • 遇到赋值语句会先计算右边所有值,在进行赋值操作

    • 交换变量的值 x, y = y, x 交换x.y的值
  • 赋值个数不一致时

    • 多了忽略
    • 少了为nil

流程控制

循环

  • while循环

    • while(condition) do statements end
  • for循环

    • 数值for循环

      • for var = exp1, exp2, exp3 do <执行体> end (var 从1,变换到2,每次变化以3 为步长,3可以不写,默认为1
      • for的三个表达式在开始循环前一次性求值,之后不再求值,像赋值操作,先计算右边的值,用临时变量存储,然后之后不再变化
    • 泛型for循环

      • 通过一个迭代器函数遍历所有值,类似java中的foreach语句

      • for i,v in ipairs(days) do print(v) end

        • ipair是lua中一个迭代器函数,用来迭代数组,不能迭代k为string类型的表
  • repeat…until

    • 在循环结束后判断条件,像do {} while
    • repeat statement until(condition)
  • 循环嵌套

  • break

判断

  • if 和elseif 后面要加then else 不需要

  • if语句

    • if(condition) then 语句 end
  • if else

    • if(condition) then 语句 else 语句 end
  • if elseif else

    • if(condition) then 语句 elseif(condition) then 语句 else 语句 end

运算符

特殊

  • 不等于 ~=

  • and or not

  • #一元运算符,返回字符串长度或者表的长度

    • #“hello” 返回5
  • … 连接两个字符串

    • a…b ,其中 a 为 "Hello " , b 为 “World”, 输出结果为 “Hello World”
  • :相当于在对象中添加函数,这个函数会传入一个self变量,通过self变量可以访问对象中的成员变量

    • function MainScene:onCreate()
    • function MainScene.onCreate(self)
    • 上面两种方法等同
  • . 相对于:只能访问表中的元素

优先级

  • ^
  • not -(负号)
    • /
  • < >= <= ~= =

  • and
  • or

字符串

字符串操作

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

  • string.lower(argument)字符串全部转化为小写字母

  • string.gsub(mainString, findString, replaceString, num) 操作的字符,被替换的字符,要替换的字符,替换的次数(不写默认全换)

  • string.find(str, substr, [init, [end]]) 在str中搜索子串, 如果搜到返回具体位置,不存在返回nil

  • string.reverse(arg) 字符串反转

  • string.format(…) 类似sprintf格式化字符串

  • string.char(arg) 和string.byte(arg[,int]) char将数字转化为字符串格式并连接, byte将字符串转化为数字,默认第一个字

  • string.len(arg) 计算字符串长度

  • string.rep(string, n)返回字符串s串联n次的所组成的字符串,参数s表示基础字符串,参数n表示赋值的次数。

  • 。。连接两个字符串

  • string.gmath(str, pattern) 查找子串,每一次调用都会返回下一个符合条件的子串,可以lua支持的符号类型

    • .(点)匹配任意字符
    • %a字母
    • %c控制字符
    • %d数字
    • %l小写字母
    • %p标点
    • %s空白字符
    • %u大写字母
    • %w数字或者字母
    • %x任何16进制数
    • %z代表0的字符
    • %x与摸个非数字非字母的字符x
  • string.match(str, pattern, init) 查找子串,只查找第一个子串,没有查到返回nil

  • string.sub(str, pos1, pos2) 截取字符串

  • tonumber(str) 将string转换成数字,当遇到"–" 无法识别的,返回nil

  • string.byte(s, i [,j]) 返回i到j的ASCII码

数组

一维数组

  • 下标不写默认从1开始,下标也可以使用负数或者任意的key
  • 遍历使用for循环 for i = 1, 3 do arrar[i] = i end

二维数组

  • 是一个包含一维数组,键值为一维数组
  • 初始化 array = {} for i = 1, 3 do array[i] = {} for j = 1, 3 do array[i][j] = i+ j end end

迭代器

泛型for

  • 迭代函数保存的三个值,通过这三个量来实现迭代

    • 迭代函数
    • 状态常量
    • 控制变量
  • for key, value in ipair(array) do print(key, value) end

  • 执行过程

    • 初始化三个值
    • 状态常量,和控制变量作为参数调用迭代函数,类似 n , i i= 0; i< n; i++
    • 将迭代函数返回值返回给变量列表 类似 key ,value = ipairs(arry)
    • 然后执行函数体
    • 返回的第一个值为nil 结束。类似 key = nil,说明数组空了

无状态的迭代器

  • ipairs()实现

    • function iter (a, i)
    • i = i + 1
      
    • local v = a[i]
      
    • if v then
      
    •    return i, v
      
    • end
      
    • end
    • function ipairs (a)
    • return iter, a, 0
      
    • end

多状态的迭代器

  • 需要保存多个状态信息,不只是状态变量和控制变量。状态变量只能从开始循环到结束,这个长度不能自己定义
  • 多状态的迭代器,可以保存多个信息。可以通过其他状态信息控制迭代器遍历,,,具体方法使用闭包函数(将匿名函数直接在再定义的时候调用。比如函数的返回值)

模块与包

把公有代码 放在同一个文件中,,将变量函数封装到表中,相当于c++的类

  • 函数可以在其他地方调用, local函数只可以在模块中调用
  • 在文件名为module.lua中定义一个module的模块
  • 创建一个table
  • 写变量、常量、函数等
  • return 这个table

require函数,加载模块,加载之后,后面就可以使用这个模块

  • require("<模块名>")
  • require"<模块名>"
  • require(module) print(module.constant)
  • 给模块起别名 local m = requir(“module”) print(m.constant)

模块加载机制

  • require函数原型

    • 先判断模块是否已经加载,package.loaded表记录已经加载过模块的名字, 和模块
    • 如果没有加载过,将模块设置为已加载,,已经加载过直接返回模块
    • 初始化模块
  • require加载路径

    • require搜索路径存在一个全局变量package.path中
    • lua启动时,会用环境变量LUA_PATH的值来初始化package.path
    • 如果没有找到该环境变量,使用编译时一个默认路径来初始化
  • package.path实例

    • package.path = “D:/lua/test/hello.lua”…";…\?.lua
    • 这个后面我不太清楚为什么要这样写
    • package.cpath = ‘/usr/local/lib/lua/5.1/?.so;’ --搜索so模块

C包

  • lua在一个叫loadlib的函数内提供 了所有动态链接功能

    • 函数有两个参数,一个是库的绝对路径,一个是初始化函数
    • local path = “/usr/local/lua/lib/libluasocket.so” local f = loadlib(path, “luaopen_socket”)
    • loadlib函数加载并连接到库,但是不打开(没有调用),返回一个初始化函数作为lua的函数,然后可以在lua中直接调用
  • 实例

    • local path = “C:\windows\luasocket.dll”,这是 Window 平台下

    • linux 平台local path = “/usr/local/lua/lib/libluasocket.so”

    • local f = assert(loadlib(path, “luaopen_socket”))

      • assert()用assert保卫代码后,遇到错误抛出异常
    • f() – 真正打开库

元表

使用场景:1.table元表实现lua中的面向对象编程 2.作为userdata的元表可以实现lua中对c中结构进行面向对象式的调用

用于对两个表操作,或者对usrdata操作,有点类似于C++运算符重载

元表调用过程

  • 执行t1 + t2过程,会调用元表的_add方法
  • 1先查看t1是否有元表,若有,查看t1的元表是否有_add方法,有则调用
  • 2先查看t2是否有元表,若有,查看t2的元表是否有_add方法,有则调用
  • 若都没有元表和_add方法,就报错

元表的元方法

  • _add+ _sub- _mul* _div/ _mod% _unm-取反
  • _concat … _eq == _lt < _le<= _call函数调用
  • _tostring转化为字符串 _index调用一个索引 _newindex给索引赋值

_index

  • 调用table一个不存在的索引时,会使用到_index元方法
  • 将表和索引作为参数传入_index方法,return一个返回值

_newindex

  • 当table中一个不存的索引赋值时,会调用_newindex元方法
  • _newindex是一个函数时会将赋值语句中的表、索引、赋值当作参数去调用,不对表进行改变
  • 当_newindex作为一个table。会将t中不存在的索引和值赋到_newindex所指向的表中

rawset

  • 直接对表中不存在的索引赋值,不会用到_newindex

rawget

  • 直接获取表中索引的实际值,不通过_newindex

协同程序

what? 协同程序coroutine 与线程比较相似

  • 独立的堆栈、局部变量、独立的指针
  • 共享全局变量和其他大部分的东西

协同程序与线程的区别

  • 一个具有多线程的程序可以同时运行几个线程,而协同程序需要彼此协作才能运行
  • 任一时刻只有一个协同程序在运行,协同程序只有在明确要求挂起时才会挂起
  • 类似有锁的多线程

基本语法

  • coroutine.create()

    • 创建coroutine,参数是一个函数,当和resume配合使用时,就要唤醒函数
  • coroutine.resume()

    • 重启,和create配合使用
  • coroutine.yield()

    • 挂起
  • coroutine.status()

    • 查看状态

      • dead
      • suspended
      • running
  • coroutine.wrap()

    • 和create功能重复
  • corroutine.running()

    • 返回正在跑的协同程序号

使用,函数传入参数等有点复杂,有时间再研究

文件I/O

简单模式

  • 只能对一个文件进行文件相关操作

  • 使用过程

    • file = io.open(fileName [,mode]) --以指定mode方式打开文件,失败返回nil和错误信息
    • io.close(file);
    • io.input(file);
    • io.output(file);
    • io.write(“hello world”) --向文件写数据
    • io.read() –

完全模式

  • file = io.open(fileName [, mode])

  • file:read()

  • file:close()

  • file:write(“hello world”)

  • file:seek(optional whence, optional offset)

    • "set"从头开始
    • “cur”从当前位置开始
    • “end”从文件尾开始
    • offset:默认为0
  • file:flush()想文件中写入缓存中的所有数据

  • io.lines(optional fileName)

错误处理

assert()

  • assert括号内包含的为true,不做任何事

  • 括号内包含的为false,打印后面添加的错误信息,返回所在行

    • assert(bool,“信息”)

error()

  • 让程序停止,进行错误处理
  • 返回error中的message作为错误信息,前面可能还会添加位置信息

pcall( )

  • 效果: 程序错误也可以运行

  • 接受一个函数类型的参数和要传递给这个函数的参数,并执行这个函数

  • 异常返回false,否则返回true

  • 使用例子

    • if pcall(functionName, …) then
    • –没有错误
    • else
    • –有错误
    • end

xpcall( )

  • 第二个参数是第一个函数发生错误时,调用的处理错误的函数

十进制十六进制转化

十六进制字符串==> 十进制数值

  • tonumber(“0x1f”, 16)
    tonumber(“1f”, 16)
    result = 31
    结果相同

十进制字符串==>十六进制字符串(有错误)

  • string.fomat("%#02x",19); – ‘0x13’
    带# — 0x
  • 不同进制的数值在内存里存储的二进制是相同的
  • 数字之间的进制转换是没有意义的

十进制字符串==>数值

  • tonumber(“23”) – 23

os库

os.date(format, temp)

  • 将(时间戳)秒数(距离1970年)转换为可读的字符串形式,默认当前时间,

  • local t1 = os.date("%Y-%m-%d-%H-%M-%S")2019-12-10-11-28-25

  • format

    • %a

星期简写,如Wed

%A

星期全称,如Wednesday

%b

月份简写,如Sep

%B

月份全称,如September

%c

日期和时间,如09/16/14 13:43:08

%d

一个月中的第几天,01-31

%H

24小时制中的小时数,00-23

%I

12小时制中的小时数,01-12

%j

一年中的第几天,001-366

%M

分钟数,00-59

%m

月份数,01-12

%p

上午(am)或下午(pm)

%S

秒数,00-59

%w

一星期中的第几天,0-6

%x

日期,如 09/16/14

%X

时间,如13:47:20

%y

两位数的年份,如14

%Y

完整的年份,如2014

%%

字符%

os.time()

  • os.time()默认返回当前时间距离1970年一月一日零点的秒数
  • os.time(tb) tb为一个表,其中year, month、day必须有,其他字段默认取12:00:00

判断表、字符串为空

a = {} if a == { } then

  • 错误,在lua中,直接对表进行数字判断,其实是对table的内存地址
  • 这个结果永远是true
  • 正确:if next(tab) == nil then

str = “” if str ~= nil then

  • 错误,在lua中,字符串不可以这样判断,这样结果永远为真
  • 正确:#str ~= 0
  • 正确:str:match("^%s") ~= nil

str = nil if str ~= nil then

  • 这个是正确的
  • str = nil 只能通过str == nil判断,不能使用# 、match判断,这样对nil操作会出错

控制浮点数精度

保留2位小数:string.format(“%.2f”, x)

会有误差

  • 数字向下取整

    • 使用%运算符,得到的结果是数字
      x%1 表示x的小数部分,x-x%1 表示x的整数部分。
      类似的,x-x%0.01 将x精确到小数点后2位。
  • 数字四舍五入

    • 在数字后面+0.05

      • length = len + 0.0005 - (len + 0.0005) % 0.001, – 控制浮点数进度为3位小数,四舍五入

完整实现

  • 四舍五入

    • — nNum 源数字
  • n 小数位数

  • -四舍五入
    function getPreciseDecimal(nNum, n)
    if type(nNum) ~= “number” then
    return nNum;
    end
    n = n or 0;
    n = math.floor(n)
    if n < 0 then
    n = 0;
    end
    local nDecimal = 10 ^ n
    local nTemp = math.floor(nNum * nDecimal + 0.5);
    local nRet = nTemp / nDecimal;
    return nRet;
    end

位运算的实现

–bit 操作

bit={data32={}}
for i=1,32 do
    bit.data32[i]=2^(32-i)
end

function bit:d2b(arg)
    local   tr={}
    for i=1,32 do
        if arg >= self.data32[i] then
        tr[i]=1
        arg=arg-self.data32[i]
        else
        tr[i]=0
        end
    end
    return   tr
end   --bit:d2b

function    bit:b2d(arg)
    local   nr=0
    for i=1,32 do
        if arg[i] ==1 then
        nr=nr+2^(32-i)
        end
    end
    return  nr
end   --bit:b2d

function    bit:_or(a,b)  
    local   op1=self:d2b(a)  
    local   op2=self:d2b(b)  
    local   r={}  
      
    for i=1,32 do  
        if  op1[i]==1 or   op2[i]==1   then  
            r[i]=1  
        else  
            r[i]=0  
        end  
    end  
    return  self:b2d(r)  
end


function  bit:_xor(a,b)
    local op1 = self:d2b(a)
    local op2 = self:d2b(b)
    local r={}
    for i = 1,32 do
        if op1[i] == 0 then
            r[i] = op2[i]
        else
            if op2[i] == 0 then
                r[i] = 1
            else
                r[i] = 0
            end
        end
    end
    return self:b2d(r)
end

function    bit:_and(a,b)
    local   op1=self:d2b(a)
    local   op2=self:d2b(b)
    local   r={}

    for i=1,32 do
        if op1[i]==1 and op2[i]==1  then
            r[i]=1
        else
            r[i]=0
        end
    end
    return  self:b2d(r)

end --bit:_and


function    bit:_rshift(a,n)
    local   op1=self:d2b(a)
    local   r=self:d2b(0)

    if n < 32 and n > 0 then
        for i=1,n do
            for i=31,1,-1 do
                op1[i+1]=op1[i]
            end
            op1[1]=0
        end
    r=op1
    end
    return  self:b2d(r)
end --bit:_rshift

function    bit:_lshift(a,n)  
    local   op1=self:d2b(a)  
    local   r=self:d2b(0)  
      
    if n < 32 and n > 0 then  
        for i=1,n   do  
            for i=1,31 do  
                op1[i]=op1[i+1]  
            end  
            op1[32]=0  
        end  
    r=op1 
    end  
    return  self:b2d(r)  
end --bit:_lshift 

function    bit:_not(a)  
    local   op1=self:d2b(a)  
    local   r={}  
  
    for i=1,32 do  
        if  op1[i]==1   then  
            r[i]=0  
        else  
            r[i]=1  
        end  
    end  
    return  self:b2d(r)  
end --bit:_not  

在表中直接添加key和value

使用tab.time = “haha” 也可以

tab[time] = “haha” 是错误的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值