lua小结

关于lua只提供了一些最基本的构建。通过这些构件lua的用户可以创造出更复杂的功能。lua提供了一种创建面向对象的能力通过table(常用的数据结构,关联数组)可以创建元素构件。

第一部分

-----lua基本语法与解释器程序------------------------------------------------------------------------------------------

1.几条连续的lua语句之间并不需要分隔符。 
2.lua中的标识符可以是由任意字母、数字和下划线构成的字符串但不能以数字开头。 
3.避免使用以一个下划线开头并跟着一个或多个大写字母例如“_VERSION”的标识符lua将这类标识符保留用作特殊用途。 
4.lua的保留字区分大小写and break do else elseif end false for function if in local nil notor repeat return then true until while 
5.两个连字符— —行注释 “— —[[”开始直至“]]”。 建议使用“— —]]” 
6.全局变量不需要声明。只需将一个值赋予一个全局变量就可以创建了。访问一个未初始化的变量不会引发错误访问结果是一个特殊值nil。若要删除某全局变量只需将其赋值为nil。 
7.局部变量使用local声明。

8.lua中有8种基础类型nil空、boolean布尔、number数字、string(字符串)、userdata(自定义类型)、function(函数)、thread(线程)和table(表)。函数type可根据一个值返回其类型。

-------lua表达式与语句---------------------------------------------------------------------------------------------------

1.算数操作符“+”、“-”、“*”、“/”、“^”、“%”、一元负号“-”。所有这些操作符都可用于实数。 
2.取模操作符的定义a%b== a - floor(a/b)*b另有math.pi 
3.关系操作符~=这些操作符结果是true或false对于table、userdata、function,lua是作引用比较的。 
4.两个数字或两个字符串可作大小比较而其他类型只能做相等性或不等性比较。会报错吗 
5.*逻辑操作符and,or和not,与条件控制语句一样所有逻辑操作符将false和nil视为假。而将其他的任何视为真。 
6.*对于and操作符若第一个操作数为假就返回第一个操作数否则返回第二个操作数。需要时才考虑第二个参数。 
7.字符串连接操作符“..”两个点若有数字会将转换成一个字符串。字符串是不可变值。 
8.优先级^ not #-算数 ..关系 逻辑。 多使用括号。 
9.table:支持嵌套

    数组:以1作为起始值,#用于返回一个数组或线性表最后一个索引值;所有未初始化的元素的索引结果都是nil但lua将nil作为界定数组的标志。如果真的需要处理含有空隙的数组可以使用函数table.maxn,它将返回一个table的最大正索引数。

     表-键值-两种读取方式 ; 链表-很少使用 
11.table构造式可以在最后一个元素后面写一个逗号然后那后面是否有元素同时在一个构造式’中还可以用分号代替逗号。通常会将分号用于分隔构造式中不同的成分。 

opnames = {[“+”] = “add”,[“-”] = “sub”, [“*”] = “mul”, [“/”] = “div”} 
i = 20; s = “-” a = {[i+0] = s, [i+1] = s..s, [i+2] = s..s..s}  print(a[22]) —> -

构造式{x=0,y=0}等价于{[“x”]=0,[“y”]=0}, 

构造式{“r”,”g”,”b”}等价于{[1]=“r”,[2]=“g”,[3]=“b”}

-------lua表达式与语句---------------------------------------------------------------------------------------------------

1.多重赋值:可以同时将多个值赋予多个变量。每个值或每个变量之间以逗号分隔。如x, y = y, x。 
2.若函数f将返回两个值a,b = f()a接收了第一个b接收了第二个。 多重赋值数量不对应可以自动调整不会报错。 
3.在lua中有一种习惯写法 local foo = foo 
4.控制结构ifthen (elseif then elseif then) else end while do end repeat until条件 

5.break for/repeat/while; return在下一个块中return就是最后一条语句do return end

6.for语句有两种形式数字型for(numeric for)和泛型forgeneric for.

泛型for循环与数字型for循环有两个相同点:1.循环变量是循环体的局部变量2.决不应该对循环变量作任何赋值。 
6.数字型for语法如下for var=exp1,expo,step do <执行体> end ;for的3个表达式是在循环开始前一次性求值的break结束循环math.huge数值上限 
7.泛型for:通过一个迭代器iterator函数来遍历所有值for i,v in ipairs(a) doprint(v) end; lua的基础库提供了impairs这是一个用于遍历数组的die迭代器函数。在每次循环中i会被赋予一个索引值同时v被赋予一个对应于该索引的数组元素值。 
8.泛型for2标准库提供了几种迭代器包括用于迭代字符串中每行的io.lines、迭代table元素的pairs、迭代数组元素的ipairs、迭代字符串中单词的(string.gmatch)等。当然读者可以编写自己的迭代器。虽然泛型for的使用非常简单但编写迭代器函数却有不少细节需要注意第七章。 
9.泛型for3假设有一个table它的内容是一周中每天的名称days = {。。。}。现在要讲一个名称转换成它在一周中的位置。 revDays = {} for k,v in pairs(days) do revDays[v] = k end 

10.如何编写泛型for的迭代器(Iterator)
 -------lua函数--------------------------------------------------------------------------------------------------

1.多重返回值:只需在return关键字后列出所有的返回值即可。 
    s, e = string.find(“hello Lua users”, “Lua”) print(s,e) —>7 9 
2.lua会调用一个函数返回值数量以适应不同的调用情况。 
    functionfoo2() return “a”,”b” end

    x,y =foo2(),20    print(x, y) —> x=“a”,y=20

    functionfoo() return end 
     x,y = foo(),20,  print(x, y)—> x=nil, y=20 
    print(foo2(),1) —> a, 1    print(foo2()..”x”) —> ax 

3.lua构造式可以完整地接收一个函数调用的所有结果。 
    t= {foo()} t = {foo2()} —>t = {“a”, “b”} 
    t = {foo(), foo2(), 4} —>t[1]= nil, t[2] = “a”, t[3] = 4 

4.reurn语句,将返回f所有的返回值。也可以将一个函数调用放入一对圆括号中,从而迫使它只返回一个结果。 
    print(foo2()) print( (foo2()) ) 
5.关于多重返回值还要介绍一个特殊函数——unpack。它接受一个数组作为参数,并从下标1开始返回该数组的所有元素: print(unpack{10,20,30})。泛型调用机制可以动态地以任何实参来调用任何函数。f(unpack(a))。 
在lua中通过递归实现效果: 
function unpack (t, i)

i = i or 1

if t[i] then return t[i], unpack(t, i+1) end

end 
6.变长参数:参数中的3个点(…)表示该函数可接受不同数量的实参。lua提供了专门用于格式化文本(string.format)和输出文本(io.write)的函数 
function add (…) local s = 0 for i,v in ipairs{…} do s = s + v end return send  
function fwrite (fmt, …) return io.write(string.format(fmt, …)) end 

7.具名实参:考虑一个函数os.rename,这个函数用于文件改名。因此,会希望这个函数能接受两个具有名称的实参。例子: 
w = Window{ x=0, y=0, width=300, height=200, title=“Lua”, background=“blue”,border=true } 

function Window (options) 
—检查必要的参数 iftype(options.title)~=“string” then error(“no title”) elseif type(options.width)~=“number”then error(“no width”) elseif type(options.height)~=“number” then error(“noheight”) end 

—其他参数都是可选的_Window(options.tilte, options.x or 0, —默认值 options.y or 0, —默认值 options.width, options.height, options.background or“while”, —默认值options.border, —默认值为false(nil)) end 

-------lua函数---------------------------------------------------------------------------------------------------

1.table库提供了一个函数table.sort,它接收一个table并对其中的元素排序。像这种函数就必须支持各种各样可能的排序准则。这个函数接受两个元素,并返回在有序情况下的第一元素是否应排在第二元素之前。例子: 
network = { {name = “grauna”, IP = “210.26.30.34”}, {name = “arraial”, IP =“210.26.30.23”}, {name = “lua”, IP = “210.26.30.12”}, {name = “derain”, IP =“210.26.30.20”}, } 
如果想以name字段、按反向的字符顺序来对这个table排序的话,只需这么写: table.sort(network, function (a,b) return (a.name> b.name) end) 
2.闭合函数:
function newCounter() 

    locali = 0 

    returnfunction ()  i = i + 1 return i end 

end 
c1 = newCounter() c2 = newCounter() 

print(c1()) —>1 print(c2())—>1 print(c1()) —>2 
3.非全局的函数:函数可以放在table中,local function f(<参数>) <函数体> end ; 
localfact = function (n) 

    if n== 0 then return 1

    elsereturn n*fact(n-1)    —因此这句是调用了一个全局的fact

    end 

end

程序错误,因为局部变量fact尚未定义完毕。可以先定义 local fact,*** 
5.正确的尾调用:不会耗费栈空间:return f(),类似于goto函数的;在lua中“尾调用”的一大应用就是编写“状态机”。

-------lua协同程序(coroutine)------------------------------------------------------------------------------------------------

从概念上几个讲讲线程与协同程序的主要区别:一个具有多个线程的程序可以同时运行几个线程;而一个具有多个协同程序的程序在任意时刻只能运行一个协同程序。

Lua将所有关于协同程序的函数放置在一个名为coroutine的table中;一个协同程序可以处于4个不同的状态:挂起(suspended)、运行(running)、死亡(dead)和正常(normal)

1.协同程序基础

    创建时,处于挂起状态:co=coroutine.create(function()print("hi") end) print(co) --》thread:0x8071d98

    通过函数status来检查协同程序状态:print(coroutine.status(co))-->suspended

    函数coroutine.resume用来启动或再启动一个协同程序的执行,并将其状态由挂起改为运行:coroutine.resume(co)    --> hi 

    协同程序的内容打印了hi便终止了,然后它就处于死亡状态:print(coroutine.status(co))-->dead

    函数yield可以让一个运行中的协同程序挂起,而之后可以在恢复它的运行。co =coroutine.create(function() for i=1,10 do print("co",i)coroutine.yield() end end) 

    注意:resume是在保护模式中运行的。因此,如果在一个协同程序的执行中发生任何错误,lua是不会显示错误消息的,而是将执行权返回给resume调用。  print(coroutine.resume(co))false cannot resume dead coroutine

    当一个协同程序A唤醒另一个协同程序B时,协同程序A就处于正常状态。

    在调用resume返回的内容中,第一个值为true表示没有错误,而后面所有的值都是对应yield传入的参数:co = coroutine.create(function(a,b)coroutine.yield(a+b, a-b) end) print(coroutine.resume(co,20,10)) -->true 3010

    与此对应的是,yield返回的额外值就是对应resume传入参数:co = coroutine.create(functionprint("co",coroutine.yield()) end) coroutine.resume("co")--> coroutine.resume("co",4,5) -->co 4 5

    最后,当一个协同程序结束时,它的主函数所返回的值都将作为对应resume的返回值:co =coroutine.create(function() return 6,7 end) print(coroutine.resume(co))-->true 6 7

2.管道(pipe)与过滤器(filter)

消费者驱动:当消费者需要一个新值时,它唤醒生产者,生产者返回一个新值后停止运行,并等待消费者再次唤醒。

functionreceive()

    localstatus,value = coroutine.resume(producer)

    returnvalue

end

functionsend(x)

    coroutine.yield(x)

end

producer= coroutine.create(    --生产者现在是一个协同程序

     function ()

        while true do

            local x=io.read() --产生新值

            send(x)

        end

    end)

functionconsumer()

    whiletrue do

        localx = receive()    --从生产者接收值

        io.write(x,"\n")    --消费新的值

    end

end

-------lua---------------------------------------------------------------------------------------------------

过滤器:一种位于生产者和消费者之间的处理功能,可用于对数据的一些变换。过滤器唤醒一个生产者促使其产生新值,然后又将变换后的值传递给消费者。

functionreceive (prod)

    localstatus,value = coroutine.resume(prod)

    returnvalue

end

functionsend(x)

    coroutine.yield(x)

end

functionproducer()

    returncoroutine.create(function()

        whiletrue do

            localx = io.read() --产生新值

            send(x)

        end

    end)

end

function filter(prod)

   returnconroutine.create(function()

    for line = 1,math.huge do

      localx = receive(prod)  --获取新值

      x = string.format("%5d%s", line, x) 

     send(x)   --将新值发送给消费者

    end

  end)

end

functionconsumer(prod)

 while true do

   local x = receive(prod)  --获取新值

   io.write(x,"\n")

 end

end

p = producer()

f = filter(p)

consumer(f)

在pipe中没想任务都在各自独立的进程中运行,而协同程序中每项任务都在各自独立的协同程序中运行。pipe在writer和reader之间提供了一个缓冲器,因此他们运行速度允许一定差异。值得注意的是,在pipe中进程间的切换代价很高。而在协同程序中,切换代价则小得多。

 

第二部分

 

-------------------------------------------------------------元表和元方法--------------------------------------------------------------------------------

1.lua中每个值都有一套预定义的操作集合,但无法将两个table相加,无法对函数作比较,无法调用一个字符串。lua中的每个值都有一个元表。table和userdata可以有各自独立的元表,而其他类型的值则共享其类型所属的单一元表。

2.lua在创建新的table时,不会创建元表: ===重载操作符
             t = {}       print(getmetatable(t) ) —>nil

setmetatable(t,t1)assert( getmetatable(t) == t1 )

3.算数类的元方法:__add,__sub,__mul,__div,__mod,__pow,__unm相反数;此外,还可以定义__concat字段,用于描述连接操作符的行为。使用函数赋值

允许混合类型运算:如果第一个值有元表,并且有相应元方法,则使用第一个的元方法,如果两个都没有,则会报错。s=s+8.
4.关系类的元方法:__eq,__lt,__le,其他没有的,lua会自行转化。使用函数赋值

不能应用于混合的类型。会报错。对于等号比较,不会报错,但等号元方法不同会直接返回false,而不是采取元方法比较。
5.库定义的元方法:

函数print总是调用__tostring的元方法,否则使用表的引用值;使用函数赋值

元表的一个字段__metatable用于保护元表,使用值赋值。getmetatable()返回该字段set引发错误。

6.table访问的元方法:改变table行为:查询table及修改table中不存在的字段。

__index元方法:当访问一个table中不存在的字段时,解释器去查找__index元方法。如果没有这个元方法,那么访问结果为nil。调用rawget(t,i)就是对tablet进行原始访问,可以仅访问原始表。

Window = {}

Window.mt = {}

function window.new(o)

 setmetatable(o, window.mt)

 return o

end

Window.prototype = {x = 0, y = 0, width= 100, height = 100}

window.mt.__index = function(table,key)

 return window.prototype(key)

end

--window.mt.__index = window.prototype

__newindex元方法:将一个table作为__index元方法是一种实现单一继承的方式。将函数作为__index元方法可以实现多重继承、缓存以及其他一些功能。调用rawset(t,i,v)就是对tablet进行原始赋值。

7.具有默认值的table:常规table中的任何字段默认都是nil。通过元表就可以很容易地修改这个默认值:例子:
local key = {} — 唯一的key,将默认值存入table中,避免创建多个元表

local mt = {__index = function (t)return t[key] end}

function setDefault (t, d)   t[key] = d  setmetatable(t, mt) end

--table = {} setDefault(table, 0)

8.跟踪table的访问:为了监视一个table的所有访问,就应该为真正的table创建一个代理。这个代理是一个空的table,其中__index和__newindex元方法可用于跟踪所有的访问,并将访问重定向到原来的table上。假设我们想跟踪tablet的访问。。。如果想要同时监视几个table,无须为每个table创建不同的元表。相反,只要以某种形式将每个代理与其table关联起来,并且所有代理都共享一个公共的元表。
local index = {} —创建私有索引
local mt = { —创建元表
__index = function (t, k) print(“*access to element ” .. tostring(k)) returnt[index][k] —访问原来的table end
__newindex = function (t, k, v) print(“*update of element ” .. tostring(k) .. “to ” .. tostring(v)) t[index][k] = v —更新原来的table end }
function track (t)  local proxy = {}proxy[index] = t  setmetatable(proxy, mt) return proxy end
现在,若要监视table t,唯一要做的就是执行:t = track(t)
9.只读的table:通过代理的概念,只需跟踪所有对table的更新操作,引发一个错误就可以了。
function readOnly (t)

 local proxy = {}

 local mt = { —创建元表 __index = t,__newindex = function (t, k ,v) error(“attempt to update a read-only table”, 2)end

setmetatable(proxy,mt)

return proxy end
创建了一个表示星期的只读table:
days = readOnly{“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”,“Friday”, “Saturday”} print(days[1]) —>Sunday days[2] = “Noday” stdin:1:attempt to update a read-only table

------------------------------------------弱引用------------------------

  1. 自动内存管理的缺陷:lua是具备自动内存管理的。我们只管创建对象,无须删除对象(当然要对无用的对象设置nil),lua会自动删除那些认为是垃圾的对象。

  2. 弱引用:给table添加__mode元方法,如果这个元方法的值包含了字符串”k”,就代表这个table的key都是弱引用的。一旦其他地方对于key值的引用取消了(设置为nil),那么,这个table里的这个字段也会被删除。

    t = {};

    -- 给t设置一个元表,增加__mode元方法,赋值为“k”
        setmetatable(t, {__mode = "k"});


        -- 使用一个table作为t的key值
        key1 = {name = "key1"};
        t[key1] = 1;
        key1 = nil;
      
        -- 又使用一个table作为t的key值
        key2 = {name = "key2"};
        t[key2] = 1;
        key2 = nil;
      
        -- 强制进行一次垃圾收集
        collectgarbage();
      
        for key, value in pairs(t) do
            print(key.name .. ":" ..value);
        end

  3. 三种形式的弱引用

    1)key值弱引用,也就是刚刚说到的情况,只要其他地方没有对key值引用,那么,table自身的这个字段也会被删除。设置方法:setmetatable(t,{__mode = “k”});
    2)value值弱引用,情况类似,只要其他地方没有对value值引用,那么,table的这个value所在的字段也会被删除。设置方法:setmetatable(t,{__mode = “v”});
    3)key和value弱引用,规则一样,但是key和value都同时生效,任意一个起作用时都会导致table的字段被删除。设置方法:setmetatable(t, {__mode = “kv”});

     

    环境:Lua将其所有的全局变量保存在一个常规的table中,这个table称为环境Lua将环境table自身保存在一个全局变量_G中。for n in pairs(_G) do print(n)end

 

 -------lua异常处理pcall、xpcall、debug--------------------------------------------------------------------------------------------------

pcall()函数:如果程序发生错误,不想让程序停止

pcall 有两个返回值 
1 : 是否正确 
2 : 错误原因

function test() print("a") print(a[1]) -- 错误,将原因打印

end

local b , r = pcall(test)

 

error函数是让程序停止,进行错误处理

error("程序发生错误")

 

xpcall(f,err)函数调用所请求的函数,并设置错误处理程序。 f内的任何错误都不会传播;

xpcall只捕获错误,使用原始错误对象调用err函数,并返回状态代码。

function myfunction () n = n/nil end

function myerrorhandler( err ) print( "ERROR:", err ) end

local status = xpcall( myfunction, myerrorhandler ) print( status)
 

 

==========================C++与Lua======================================

1.使用工具tolua++

2.堆栈和全局表

lua是C语言库,所以在C++中使用必须用extern “C”声明,让编译器知道引入lua库

extern "C"{

#include<lua.h>

#include<lualib.h>

#include<luaxlib.h>

};

init()

{

lua_state *pL = lua_open();

luaopen_base(pL);

luaopen_math(pL);

luaopen_string(pL);

/*

执行lua脚本,返回0代表成功

重置栈顶索引

判断栈顶的值的类型是否为string,返回非0值代表成功

获取栈顶的值

int err = lua_dofile(pL,"HelloLua.lua");

lua_settop(pL); lua_getglobal(pL,"myName");

int isstr = lua_isstring(pL,1);

if(isstr == 0) {const char* str = lua_tostring(pL,1);}

栈顶的索引方式有两种: 正数索引1;负数索引-1

*/

lua_close(pL);

//lua有一些标准库,要使用这些库,就要用luaopen_*去加载;用lua_close来释放内存。

}

3.c++调用lua

--helloLua.lua文件 C++ "name"  -1             lua

myName = "beauty" helloTable变量-2

helloTable={name="mutou",IQ=125}

//bool HelloLua::init()

{

-----

lua_getglobal(pL,"helloTable");

lua_pushstring(pL,"name");

lua_gettable(pL,-2);

//现在表中name对应的值已经在栈顶

const char* sName = lua_tostring(pL,-1)

------

lua_getglobal(pL,"HelloAdd");

lua_pushnumber(pL,10);

lua_pushnumber(pL,5);

lua_call(pL,2,1);

//执行函数的参数个数,返回值个数

int iResult = lua_tonumber(pL, -1);

}

4.lua调用C++

static int getNumber(int num) {return num+1}

static int cpp_GetNumber(lua_State* pL) 

{

//从栈中取一个值

int num = (int)lua_tonumber(pL,1);

//调用getNumber函数,将返回值入栈

lua_pushnumber(pL,getNumber(num));

//返回值个数,getNumber只有一个返回值

return 1;

}

bool HelloLua::init()

{

-------

//C++的函数和封装函数都必须是静态的

lua_register(pL,"cpp_GetNumber",cpp_GetNumber);

}

--lua文件中: local num = cpp_GetNumber(10)

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值