Lua 常用工具类代码记录(一)

 

 

 

//Lua正则表达式如何匹配中文///

function CheckChinese(s)

    local ret = {};

    local f = '[%z\1-\127\194-\244][\128-\191]*';

    local line, lastLine, isBreak = '', false, false;

    for v in s:gfind(f) do

        table.insert(ret, {c=v,isChinese=(#v~=1)});

    end

    return ret;

end

for k, v in ipairs(CheckChinese('a中文b+')) do

    print(k,v.c,#v.c,v.isChinese);

end

-------------------

1    a    1    false

2    中    3    true

3    文    3    true

4    b    1    false

5    +    1    false

匹配中英文混合输入的代码

function dlg:onBtnOKClick()

    local input = self:getInputNewContent()

    if input == "" then

        flytip("输入不能为空")

        return

    end

    if self.pattern then

        if self.isContainChinese then

            local charCount = 0

            for i, v in ipairs(self:CheckChinese(input)) do

                if v.isChinese then

                    charCount = charCount + 2

                else

                    charCount = charCount + 1

                    if string.find(v.c, self.pattern) == nil then

                        flytip("抱歉,您输入的内容含有非法字符或屏蔽词,请重新输入。")

                        return

                    end

                end

            end

            if self.characterLimit then

                if charCount > self.characterLimit then

                    flytip("抱歉,您输入的字符长度有误,请重新输入。")

                    return

                end

            end

        else

            if string.find(input, self.pattern) == nil then

                flytip("抱歉,您输入的内容含有非法字符或屏蔽词,请重新输入。")

                return

            end

        end

    end

   

    self:Hide()

    if self._onOK then

        self._onOK()

    end

end

//Lua正则表达式如何匹配中文///

//Lua的时间和时间戳相互转换///

1、时间戳转换成时间

local t = 1412753621000

function getTimeStamp(t)

    return os.date("%Y%m%d%H",t/1000)

end

print(getTimeStamp(t))

 

2、得时间戳

 

os.time() -- 当前时间戳

os.time({day=17, month=5, year=2012, hour=0, minute=0, second=0}) -- 指定时间的时间戳

3、时间格式 yyyyMMddHHmmss

print(os.date("%Y%m%d%H%M%S", os.time()))

常用时间函数

print(os.time()) --当前系统时间值 1456368102  
print(os.date("%Y%m%d",os.time())) --当前系统时间的格式化字符串 20160225  
print(os.date("*t"), os.time())  --当前系统时间表  

table完整版本:

 {year=2005, month=11, day=6, hour=22,min=18,sec=30,isdst=false}

分别是:年 小时 分钟 是否夏令时

字符串转时间值

 

function string2time( timeString )  
    local Y = string.sub(timeString , 1, 4
    local M = string.sub(timeString , 5, 6
    local D = string.sub(timeString , 7, 8
    return os.time({year=Y, month=M, day=D, hour=0,min=0,sec=0})  
end  
  
local sTime = "20160202"  
local timeVal = string2time(sTime)  
print(sTime)  
print(timeVal)

 

输出结果:

20160202
1454371200

思路:

字符串转换成tabletable转换到时间值

Lua获取系统时间和时间格式化方法及格式化参数

一、系统当前时间对应的时间戳

复制代码代码如下:


local ntime = os.time
print(ntime)


二、格式化时间显示,参考下表
常用于设置header

复制代码代码如下:


ngx.header["Last-Modified:"]  = os.date("%a, %d %b %Y %X GMT")
ngx.header["Content-Type"]    = "text/html; charset=utf-8"
ngx.header["Cache-Control"]   = "public"
ngx.header["Expires"]         = os.date("%a, %d %b %Y %X GMT", os.time() + cdnCacheTime)

 

%a

abbreviated weekday name (e.g., Wed)

%A

full weekday name (e.g., Wednesday)

%b

abbreviated month name (e.g., Sep)

%B

full month name (e.g., September)

%c

date and time (e.g., 09/16/98 23:48:10)

%d

day of the month (16) [01-31]

%H

hour, using a 24-hour clock (23) [00-23]

%I

hour, using a 12-hour clock (11) [01-12]

%M

minute (48) [00-59]

%m

month (09) [01-12]

%p

either "am" or "pm" (pm)

%S

second (10) [00-61]

%w

weekday (3) [0-6 = Sunday-Saturday]

%x

date (e.g., 09/16/98)

%X

time (e.g., 23:48:10)

%Y

full year (1998)

%y

two-digit year (98) [00-99]

%%

the character '%'

 

 

// Lua的时间和时间戳相互转换///

//Lua的math函数库及一些自定义扩展///

math函数

name        describe   e.g   result

abs   取绝对值         math.abs(-2015)      2015

ceil   向上取整         math.ceil(20.15)      21

floor 向下取整         math.floor(20.15)    20

max 取最大值         math.max(20, 15) 2 0

min  取最小值         math.min(20, 15)     15

pi      圆周率     math.pi    3.14…

pow 计算x的y次幂      math.pow (2, 15)     32768

sqrt  开平方     math.sqrt(1024)       32

fmod         取模         math.fmod(20, 15)  5

modf         取整数,小数部分    math.modf(20.15)   20 15

randomseed     设随机数种子         math.randomseed(os.time()) 

random    取随机数         math.random(2, 15)         2~15

rad   角度转弧度     math.rad(180) math.pi

deg  弧度转角度     math.deg(math.pi)  180

exp   e的x次方       math.exp(4)     e ^ 4

log    x的自然对数  math.log(e ^ 4)         4

log10        10为底,x的对数 math.log10(1000)    3

frexp         格式化x * (2 ^ y)    math.frexp(160)       0.625 8

ldexp         计算 x * (2 ^ y)        math.ldexp(0.625,8)         160

sin    正弦         math.sin(math.rad(30))   0.5

cos   余弦         math.cos(math.rad(60))  0.5

tan   正切         math.tan(math.rad(45))  1

asin  反正弦     math.deg(math.asin(0.5))        30

acos 反余弦     math.deg(math.acos(0.5))       60

atan 反正切     math.deg(math.atan(1)) 45

math.huge        最大数             

math.randomseed(os.time())                    

i=math.random(1,6)                           

自定义扩展

-- 最小数值和最大数值指定返回值的范围。

-- @function [parent=#math] clamp

function math.clamp(v, minValue, maxValue) 

    if v < minValue then

        return minValue

    end

    if( v > maxValue) then

        return maxValue

    end

    return v

end

-- 根据系统时间初始化随机数种子,让后续的 math.random() 返回更随机的值

-- @function [parent=#math] newrandomseed

 

function math.newrandomseed()

    local ok, socket = pcall(function()

        return require("socket")

    end)

 

    math.randomseed(os.time())

 

    math.random()

    math.random()

    math.random()

    math.random()

end

-- 对数值进行四舍五入,如果不是数值则返回 0

-- @function [parent=#math] round

-- @param number value 输入值

-- @return number#number

 

function math.round(value)

    value = tonumber(value) or 0

    return math.floor(value + 0.5)

end

-- 角度转弧度

-- @function [parent=#math] angle2radian

 

function math.angle2radian(angle)

    return angle*math.pi/180

end

-- 弧度转角度

-- @function [parent=#math] radian2angle

 

function math.radian2angle(radian)

    return radian/math.pi*180

end

// Lua的math函数库及一些自定义扩展///

//Lua实现计算utf8字符串的长度///

function string.utf8len(input)

    local len  = string.len(input)

    local left = len

    local cnt  = 0

    local arr  = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}

    while left ~= 0 do

        local tmp = string.byte(input, -left)

        local i   = #arr

        while arr[i] do

            if tmp >= arr[i] then

                left = left - i

                break

            end

            i = i - 1

        end

        cnt = cnt + 1

    end

    return cnt

end

//Lua实现计算utf8字符串的长度///

//获取table中最大值///

function table_maxn(t)
  local mn=nil;
  for k, v in pairs(t) do
    if(mn==nil) then
      mn=v
    
end
    if mn < v then
      mn = v
    
end
  end
  return mn
end
tbl = {[1] = 2, [2] = 6, [3] = 34, [26] =5}
print("tbl 最大值:", table_maxn(tbl))
print("tbl 长度 ", #tbl)

//获取table中最大值///

//Lua封装位运算///

1.移位运算基础

 

--与   同为1,则为1

 

--或   有一个为1,则为1 

 

--非   true为 false,其余为true

 

--异或 相同为0,不同为1

 

 

--ZZMathBit = {}

 

function ZZMathBit.__andBit(left,right)    --与

    return (left == 1 and right == 1) and 1 or 0

end

 

function ZZMathBit.__orBit(left, right)    --或

    return (left == 1 or right == 1) and 1 or 0

end

 

function ZZMathBit.__xorBit(left, right)   --异或

    return (left + right) == 1 and 1 or 0

end

 

function ZZMathBit.__base(left, right, op) --对每一位进行op运算,然后将值返回

    if left < right then

        left, right = right, left

    end

    local res = 0

    local shift = 1

    while left ~= 0 do

        local ra = left % 2    --取得每一位(最右边)

        local rb = right % 2  

        res = shift * op(ra,rb) + res

        shift = shift * 2

        left = math.modf( left / 2)  --右移

        right = math.modf( right / 2)

    end

    return res

end

 

function ZZMathBit.andOp(left, right)

    return ZZMathBit.__base(left, right, ZZMathBit.__andBit)

end

 

function ZZMathBit.xorOp(left, right)

    return ZZMathBit.__base(left, right, ZZMathBit.__xorBit)

end

 

function ZZMathBit.orOp(left, right)

    return ZZMathBit.__base(left, right, ZZMathBit.__orBit)

end

 

function ZZMathBit.notOp(left)

    return left > 0 and -(left + 1) or -left - 1

end

 

function ZZMathBit.lShiftOp(left, num)  --left左移num位

    return left * (2 ^ num)

end

 

function ZZMathBit.rShiftOp(left,num)  --right右移num位

    return math.floor(left / (2 ^ num))

end

 

function ZZMathBit.test()

    print( ZZMathBit.andOp(65,0x3f))  --65 1000001    63 111111

    print(65 % 64)

    print( ZZMathBit.orOp(5678,6789))

    print( ZZMathBit.xorOp(13579,2468))

    print( ZZMathBit.rShiftOp(16,3))

    print( ZZMathBit.notOp(-4))

 

    print(string.byte("abc",1))

end

cclog("aaaaaaa:")

ZZMathBit.test()

 

[LUA-print] aaaaaaa:

 

[LUA-print] 1

 

[LUA-print] 1

 

[LUA-print] 7855

 

[LUA-print] 15535

 

[LUA-print] 2

 

[LUA-print] 3

 

[LUA-print] 97

 

 

 

2.红点

 

function GlobalService:hasRedpointEmail( )

    return ZZMathBit.andOp( ServerData.redPointStatus, dyt.RedPointStatus.Email )  ~= 0

end

 

-- 红点状态

dyt.RedPointStatus = {

    Email = ZZMathBit.lShiftOp( 1, 0 ),  --邮件

    DailyTask = ZZMathBit.lShiftOp( 1, 1),  -- 每日任务

    CommonTask = ZZMathBit.lShiftOp( 1, 2 ), --任务

    Draw = ZZMathBit.lShiftOp( 1, 3 ), --抽卡

    Sign = ZZMathBit.lShiftOp( 1, 4 ), --签到

    Activity = ZZMathBit.lShiftOp( 1, 5 ), --7日活动

    Legion = ZZMathBit.lShiftOp( 1, 6 ), --军团

    Escort = ZZMathBit.lShiftOp( 1, 7 ), --护航

    Collect = ZZMathBit.lShiftOp( 1, 9 ), --委派

    Union = ZZMathBit.lShiftOp( 1 , 10 ), --联动

    SingleRecharge = ZZMathBit.lShiftOp( 1 , 11 ), --限时活动单笔充值

    TotalRecharge = ZZMathBit.lShiftOp( 1 , 12 ), --限时活动累计充值

    TotalCost = ZZMathBit.lShiftOp( 1 , 13 ), --限时活动累计消费

    OilCost = ZZMathBit.lShiftOp( 1 , 14 ), --限时活动燃油消耗

    TotalDraw = ZZMathBit.lShiftOp( 1 , 15 ), --限时活动金币抽卡

    Qming = ZZMathBit.lShiftOp( 1 , 16 ), --限时活动清明好礼

    BaoXiang = ZZMathBit.lShiftOp( 1 , 19 ), --限时活动金币抽卡宝箱

    LevelFast = ZZMathBit.lShiftOp( 1 , 20 ), --快速补给活动

    LevelFastRed = ZZMathBit.lShiftOp( 1 , 21 ), --快速补给活动红点

}

因此都是先左移然后表示一个唯一状态。

 

红点状态:利用与运算  同为1,则为1,用一个32位整数来表示活动。

//Lua封装位运算///

//Lua用Split函数分割字符串///

function Split(szFullString, szSeparator) 

local nFindStartIndex = 1 

local nSplitIndex = 1 

local nSplitArray = {} 

while true do 

   local nFindLastIndex = string.find(szFullString, szSeparator, nFindStartIndex) 

   if not nFindLastIndex then 

    nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, string.len(szFullString)) 

    break 

   end 

   nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, nFindLastIndex - 1) 

   nFindStartIndex = nFindLastIndex + string.len(szSeparator) 

   nSplitIndex = nSplitIndex + 1 

end 

return nSplitArray 

end

用法:

local list = Split("abc,123,345", ",")

然后list里面就是

abc
123
345

第二个参数可以是多个字符,但是不能是Lua正则表达式。例如. ,或者 %w 之类的。

//Lua用Split函数分割字符串///

//Lua中number转换各种进制以及string转number///

--region : NumConvert.lua

--Date   : 2017-5-11

--Author : david

 

-- Bin 2

-- Oct 8

-- Dec 10

-- Hex 16

 

local _convertTable = {

    [0] = "0",

    [1] = "1",

    [2] = "2",

    [3] = "3",

    [4] = "4",

    [5] = "5",

    [6] = "6",

    [7] = "7",

    [8] = "8",

    [9] = "9",

    [10] = "A",

    [11] = "B",

    [12] = "C",

    [13] = "D",

    [14] = "E",

    [15] = "F",

    [16] = "G",

}

 

local function GetNumFromChar(char)

    for k, v in pairs(_convertTable) do

        if v == char then

            return k

        end

    end

    return 0

end

 

local function Convert(dec, x)

 

    local function fn(num, t)

        if(num < x) then

            table.insert(t, num)

        else

            fn( math.floor(num/x), t)

            table.insert(t, num%x)

        end

    end

   

    local x_t = {}

    fn(dec, x_t, x)

 

    return x_t

end

 

function ConvertDec2X(dec, x)

    local x_t = Convert(dec, x)

 

    local text = ""

    for k, v in ipairs(x_t) do

        text = text.._convertTable[v]

    end

    return text

end

 

function ConvertStr2Dec(text, x)

    local x_t = {}

    local len = string.len(text)

    local index = len

    while ( index > 0) do

        local char = string.sub(text, index, index)

        x_t[#x_t + 1] = GetNumFromChar(char)

        index = index - 1

    end

 

    local num = 0

    for k, v in ipairs(x_t) do

        num = num + v * math.pow(x, k - 1)

    end

    return num

end

 

--endregion

 

/Lua中number转换各种进制以及string转number /

//

//在Lua中计算中文字符串的长度///

 

--[[

    @desc: 计算字符串字符个数

    author:{author}

    time:2017-12-29 16:08:11

    --@inputstr: 源字符串

    return 字符个数

]]

function getStringCharCount(str)

    local lenInByte = #str

    local charCount = 0

    local i = 1

    while (i <= lenInByte)

    do

        local curByte = string.byte(str, i)

        local byteCount = 1;

        if curByte > 0 and curByte <= 127 then

            byteCount = 1                                            --1字节字符

        elseif curByte >= 192 and curByte < 223 then

            byteCount = 2                                            --双字节字符

        elseif curByte >= 224 and curByte < 239 then

            byteCount = 3                                            --汉字

        elseif curByte >= 240 and curByte <= 247 then

            byteCount = 4                                            --4字节字符

        end

       

        local char = string.sub(str, i, i + byteCount - 1)

        i = i + byteCount                                  -- 重置下一字节的索引

        charCount = charCount + 1                         -- 字符的个数(长度)

    end

    return charCount

end

///

 

 

// LUA解析json /

需要修改的json数据gui-config.json

{

        "configs": [{

               "server": "JP3.ISS.TF",

               "server_port": 443,

               "password": "58603228",

               "method": "aes-256-cfb",

               "remarks": ""

        },

        {

               "server": "US1.ISS.TF",

               "server_port": 443,

               "password": "37382928",

               "method": "aes-256-cfb",

               "remarks": ""

        },

        {

               "server": "HK2.ISS.TF",

               "server_port": 8989,

               "password": "59434206",

               "method": "aes-256-cfb",

               "remarks": ""

        }],

        "strategy": null,

        "index": 0,

        "global": false,

        "enabled": true,

        "shareOverLan": false,

        "isDefault": false,

        "localPort": 1080,

        "pacUrl": null,

        "useOnlinePac": false,

        "availabilityStatistics": false

}

 

LUA解析代码:

function FileRead()

        local file = io.open("gui-config.json", "r");

        local json = file:read("*a");

        file:close();

        return json;

end

 

function FileWrite()

        local file = io.open("gui-config.json", "w");

        file:close();

end

 

local cjson = require("cjson");

local file = FileRead();

local json = cjson.decode(file);

for i, w in ipairs(json.configs) do

        print("server: " .. w.password)

        print("server_port: " .. w.server_port)

        print("password: " .. w.password)

        print("method: " .. w.method .. '\n')

end


 

输出:

Lua 常用工具类代码记录(一)_第1张图片

 

// LUA解析json /

 

 

Lua 闭包

Lua中的闭包

  相信,对于经常使用Javascript的前端开发者来说,闭包这个概念一定不会陌生,在Javascript开发中,一些高级的应用都需要闭包来实现。而对于传统的C++开发者或者C#开发者来说,闭包这个词或多或少都会有些玄之又玄的感觉。那么,在开讲之前,让我们先来了解几个Lua中基础知识和概念,这样有助于我们理解Lua闭包。

1.一些前提概念

  词法定界:当一个函数内嵌套另一个函数的时候,内函数可以访问外部函数的局部变量,这种特征叫做词法定界。如下面这段代码,func2作为func1的内嵌函数,可以自由地访问属于func1的局部变量i : 

 

function func1()
    local i = 100  --upvalue
    local func2 = function()
        print(i+1)
    end
    i = 101
    return func2
end
 
local f = func1()
print(f())    --输出102

 

  第一类值:在Lua中,函数是一个值,它可以存在于变量中、可以作为函数参数,也可以作为返回值return。还是以上面的代码举例,我们将一个内嵌在func1中的函数赋值给局部变量func2,并将func2这个变量在函数结尾return

  upvalue:内嵌函数可以访问外部函数已经创建的局部变量,而这些局部变量则称为该内嵌函数的外部局部变量(即upvalue)。在我们的第一个例子中,func1的局部变量i就是内嵌函数func2upvalue

2.什么是Lua闭包

  好了有了以上的概念以后,我们也该引入Lua中闭包的概念了。闭包是由函数和与其相关的引用环境组合而成的实体,闭包=函数+引用环境

  在第一个例子中,func1函数返回了一个函数,而这个返回的函数就是闭包的组成部分中的函数;引用环境就是变量i所在的环境。实际上,闭包只是在形式和表现上像函数,但实际上不是函数。我们都知道,函数就是一些可执行语句的组合体,这些代码语句在函数被定义后就确定了,并不会再执行时发生变化,所以函数只有一个实例。而闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例,就好比相同的类代码,可以创建不同的类实例一样。

  用一句比较通俗和不甚严谨的话来讲:子函数可以使用父函数中的局部变量,这种行为就叫做闭包。这种说法其实就说明了闭包的一种表象,让我们从外在形式上,能更好的理解什么是闭包。

  对于学习C++或者是C#之类的语言入门的朋友,可能对闭包理解起来比较吃力(至少马三是这样,一会明白一会糊涂,看了很多文章、写了很多代码以后才理解,笨得要命~ o()o)。其实我们可以把Lua中的闭包和C++中的类做一下类比。闭包是数据和行为的结合体,这就好比C++中的类,有一些成员变量(Lua中的upvalue)+成员方法(Lua中的内嵌函数)。这样就使得闭包具有较好的抽象能力,在某些场合下,我们需要记住某次调用函数完成以后数据的状态,就好比C++中的static类型的变量,每次调用完成以后,static类型的变量并不会被清除。使用闭包就可以很好的完成该功能,比如利用Lua闭包特性实现一个简单地迭代器,在下面的小节中我们会介绍到。

3.典型Lua闭包例子

  1.闭包的数据隔离

 

function counter()
    local i = 0
    return function() --匿名函数,闭包
        i = i + 1
        return i
    end
end
 
counter1 = counter()
counter2 = counter() 
-- counter1,counter2 是建立在同一个函数,同一个局部变量的不同实例上面的两个不同的闭包
--                   闭包中的upvalue各自独立,调用一次counter()就会产生一个新的闭包
print(counter1()) -- 输出1
print(counter1()) -- 输出2
print(counter2()) -- 输出1
print(counter2()) -- 输出2

 

  上面的代码中,注释已经解释地很详细了。尽管看起来counter1,counter2是由同一个函数和同一个局部变量创建的闭包。但是其实它们是不同实例上面的两个不同的闭包。闭包中的upvalue各自独立,调用一次counter()就会产生一个新的闭包。有点像工厂函数一样,每调用一次counter()都会new出来一个新的对象,不同的对象之间的数据,当然也就是隔离的了。

  2.闭包的数据共享

 

function shareVar(n)
    local function func1()
        print(n)
    end
    
    local function func2()
        n = n + 10
        print(n)
    end
    return func1,func2
end
 
local f1,f2 = shareVar(1024) --创建闭包,f1,f2两个闭包共享同一份upvalue
 
f1() -- 输出1024
f2() -- 输出1034
f1() -- 输出1034
f2() -- 输出1044

 

  乍一看起来,这个概念和第一个概念矛盾啊,其实他们之间并不矛盾。在Lua中,同一闭包创建的其他的闭包共享一份upvalue。闭包在创建之时其需要的变量就已经不在堆栈上,而是引用更外层外部函数的局部变量(upvalue)。在上面的例子中,f1,f2共享同一份upvalue,这是因为f1f2都是由同一个闭包shareVar(1024)创建的,所以他们引用的upvalue(变量n)实际也是同一个变量,而它们的upvalue引用都会指向同一个地方。说白了就是func1func2的引用环境是一样,它们的上下文是一样的。再类比一下我们比较熟悉的C++,就好比C++类中有两个不同的成员函数,它们都可以对类中的同一个成员变量进行访问和修改。这第二点概念尤其要和第一点概念进行区分,它们很容易混淆。

  3.利用闭包实现迭代器功能

 

--- 利用闭包实现iterator,iterator是一个工厂,每次调用都会产生一个新的闭包,该闭包内部包括了upvalue(t,i,n)
--- 因此每调用一次该函数都会产生闭包,那么该闭包就会根据记录上一次的状态,以及返回table中的下一个元素
function iterator(t)
    local i = 0
    local n = #t
    return function()
        i = i + 1
        if i <= n then
            return t[i]
        end
    end
end
 
testTable = {1,2,3,"a","b"}
 
-- while中使用迭代器
iter1 = iterator(testTable) --调用迭代器产生一个闭包
while true do
    local element = iter1()
    if nil == element then
        break;
    end
    print(element)
end
 
-- for中使用迭代器
for element in iterator(testTable) do --- 这里的iterator()工厂函数只会被调用一次产生一个闭包函数,后面的每一次迭代都是用该闭包函数,而不是工厂函数
    print(element)
end

 

  利用闭包我们可以很方便地实现一个迭代器,例如上面代码中的iteratoriterator是一个工厂,每次调用都会产生一个新的闭包,该闭包内部包括了upvalue(t,i,n),因此每调用一次该函数都会产生闭包,那么该闭包就会根据记录上一次的状态,以及返回table中的下一个元素,从而实现了迭代器的功能。需要额外注意的是:迭代器只是一个生成器,他自己本身不带循环。我们还需要在循环里面去调用它才行。

  while循环的那段例子代码中,我们首先调用迭代器创建一个闭包,然后不断地调用它就可以获取到表中的下一个元素了,就好像是游标一样。而由于 for ... in ... do 的这种写法很具有迷惑性,所以在for循环中使用迭代器的话,我们需要注意:这里的iterator()工厂函数只会被调用一次产生一个闭包函数,后面的每一次迭代都是用该闭包函数,而不是工厂函数。相信许多朋友此时会和马三一样产生一个疑问,为什么在for循环中使用迭代器,iterator()工厂函数只会被调用一次呢?难道不是每次判断执行条件的时候都去执行一次iterator函数吗?其实这和Lua语言对for...in...do这种控制结构的内部实现方式有关。for in在自己内部保存三个值:迭代函数、状态常量、控制变量。for...in 这种写法其实是一种语法糖,在Programming in Lua中给出的等价代码是:

 

do
    local _f,_s,_var = iter,tab,var
    while true do
        local _var,value = _f(_s, _var)
        if not _var then break end
        body
    end
end

 

  怎么样,for...in 的内部实现代码和我们在while中调用Iterator的方式是不是很类似?Iterator(table)函数返回一个匿名函数作为迭代器,该迭代函数会忽略掉传给它的参数tableniltable和控制变量已被保存在迭代函数中,因此将上面的for循环展开后应该是这个样子:

 

iter = iterator(testTable)
element,value = iter(nil,nil)--忽略参数,value置为nil
if(element) then
    repeat
        print(element)
        element,value = iter(nil,element)--忽略参数
    until(not element)
end

 

回到顶部

三、C#中的闭包

  我们在上面花了很大的篇幅来介绍Lua的闭包,其实在C#中也是有闭包概念的。由于我们已经有了之前的Lua闭包基础,所以再理解C#中的闭包概念也就不那么困难了。照例在开讲之前我们还是先介绍一些C#中的基础知识与概念,一边有助于我们的理解。

1.一些前提概念

  变量作用域:C#里面,变量作用域有三种,一种是属于类的,我们常称之为field(字段/属性);第二种则属于函数的,我们通常称之为局部变量;还有一种,其实也是属于函数的,不过它的作用范围更小,它只属于函数局部的代码片段,这种同样称之为局部变量。这三种变量的生命周期基本都可以用一句话来说明,每个变量都属于它所寄存的对象,即变量随着其寄存对象生而生和消亡。

  对应三种作用域我们可以这样说,类里面的变量是随着类的实例化而生,同时伴随着类对象的资源回收而消亡(当然这里不包括非实例化的staticconst对象)。而函数(或代码片段)的变量也随着函数(或代码片段)调用开始而生,伴随函数(或代码片段)调用结束而自动由GC释放,它内部变量生命周期满足先进后出的特性。

  那么,有没有例外的情况呢?答案当然是有的,它就是我们的今天的主角:C#闭包。

  委托:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。(关于委托的讲解,网上已经有很多文章了,这里不再赘述,笼统一点你可以把委托简单地理解为函数指针)

2.什么是C#闭包?

  闭包其实就是使用的变量已经脱离其作用域,却由于和作用域存在上下文关系,从而可以在当前环境中继续使用其上文环境中所定义的一种函数对象。(本质上和Lua闭包的概念没有什么不同,只是换种说法罢了)

3.典型的C#闭包例子

  首先让我们来看下面这一段C#代码:

 

public class TCloser
{
    public Func<int> T1()
    {
        var n = 999;
        return () =>
        {
            Console.WriteLine(n);
            return n;
        };
    }
}
 
class Program
{
    static void Main()
    {
        var a = new TCloser();
        var b = a.T1();
        Console.WriteLine(b());
    }
}

 

  从上面的代码我们不难看到,变量n实际上是属于函数T1的局部变量,它本来的生命周期应该是伴随着函数T1的调用结束而被释放掉的,但这里我们却在返回的委托b中仍然能调用它,这里正是C#闭包的特性。在T1调用返回的匿名委托的代码片段中我们用到了n,而在编译器看来,这些都是合法的,因为返回的委托b和函数T1存在上下文关系,也就是说匿名委托b是允许使用它所在的函数或者类里面的局部变量的,于是编译器通过一系列操作使b中调用的函数T1的局部变量自动闭合,从而使该局部变量满足新的作用范围。

  所以对于C#中的闭包,你就可以像之前介绍的Lua闭包那样理解它。由于返回的匿名函数对象是在函数T1中生成的,因此相当于它是属于T1的一个属性。如果你把T1的对象级别往上提升一个层次就很好理解了,这里就相当于T1是一个类,而返回的匿名对象则是T1的一个属性,对属性而言,它可以调用它所寄存的对象T1的任何其他属性或者方法,包括T1寄存的对象TCloser内部的其他属性。如果这个匿名函数会被返回给其他对象调用,那么编译器会自动将匿名函数所用到的方法T1中的局部变量的生命周转期自动提升,并与匿名函数的生命周期相同,这样就称之为闭合。

 

 

///

 

从Lua查找表元素的过程看元表、元方法/

 

一、什么是元表

Lua table中我们可以访问对应的key来得到value值,但是却无法对两个table进行操作。因此Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。通俗来说,元表就像是一个操作指南,里面包含了一系列操作的解决方案,例如__index方法就是定义了这个表在索引失败的情况下该怎么办,__add方法就是告诉table在相加的时候应该怎么做。这里面的__index__add就是元方法,下面我们详细解读一下元方法。

回到顶部

二、什么是元方法

通过上面的知识,我们知道了通过使用元表可以定义Lua如何计算两个table的相加操作。当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add"等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。很多人对Lua中的元表和元方法都会有一个这样的误解:如果A的元表是B,那么如果访问了一个A中不存在的成员,就会访问查找B中有没有这个成员。如果说这样去理解的话,就大错特错了,实际上即使将A的元表设置为B,而且B中也确实有这个成员,返回结果仍然会是nil,原因就是B__index元方法没有赋值。别忘了我们之前说过的:元表是一个操作指南,定义了元表,只是有了操作指南,但不应该在操作指南里面去查找元素,而__index方法则是操作指南索引失败时该怎么办。下面我们通过几段实际的代码来看一下Lua的表元素的查找过程以便更深入地体会上述这些概念。

下面是一些Lua表中可以重新定义的元方法:

 

__add(a, b) --加法
__sub(a, b) --减法
__mul(a, b) --乘法
__div(a, b) --除法
__mod(a, b) --取模
__pow(a, b) --乘幂
__unm(a) --相反数
__concat(a, b) --连接
__len(a) --长度
__eq(a, b) --相等
__lt(a, b) --小于
__le(a, b) --小于等于
__index(a, b) --索引查询
__newindex(a, b, c) --索引更新(PS:不懂的话,后面会有讲)
__call(a, ...) --执行方法调用
__tostring(a) --字符串输出
__metatable --保护元表

 

回到顶部

三、Lua的表元素查找机制

众所周知,Lua的表本质其实是个类似Dictionary的东西,其元素是很多的Key-Value键值对。如果尝试访问了一个表中并不存在的元素时,就会触发Lua的一套查找机制,Lua也是凭借这个机制来模拟了类似的行为。下面是一段简单地访问表中元素的代码:

myTable = {
    prop1 = 'Property',
}
print (myTable.prop1)
print (myTable.prop2)  --打印不存在的成员prop2

稍微有些Lua语法的同学,一看就可以看出,上面的输出结果为:Property  nil 。输出为nil的原因很简单,myTable中并没有prop2这个成员,这符合我们平时操作Dictionary的习惯。但对于Lua的表,如果myTable有元表和元方法,情况就不同了。下面我们再看一下设置了元表和元方法的代码:

 

father = {  
    prop1=1  
son = {  
    prop2=1  
setmetatable(son, father) --把son的metatable设置为father  
print (son.prop1) 

 

执行输出的结果仍然为:nil,这正印证了上面所说的,只设置元表是不管用的。再来看看同时设置元表和对应的元方法的代码:

 

father = {  
    prop1=1  
father.__index = father -- 把father的__index方法指向它本身
son = {  
    prop2=1  
setmetatable(son, father) --把son的metatable设置为father  
print (son.prop1)

 

执行输出的结果为:1

结合上述的几个小例子,我们再来解释一下__index元方法的含义:在上面的例子中,当访问son.prop1时,son中是没有prop1这个成员的。接着Lua解释器发现son设置了元表:father,(需要注意的是:此时Lua并不是直接在fahter中找到名为prop1的成员,而是先调用father__index方法),如果__index方法为nil,则直接返回nil。如果__index指向了一张表(上面的例子中father__index指向了自己本身),那么就会到__index方法所指向的这个表中去查找名为prop1的成员。最终,我们在father表中找到了prop1成员。这里的__index方法除了可以是一个表,也可以是一个函数,如果是函数的话,__index方法被调用时会返回该函数的返回值。

Lua查找一个表元素的规则可以归纳为如下几个步骤:

  • Step1:在表自身中查找,如果找到了就返回该元素,如果没找到则执行Step2
  • Step2:判断该表是否有元表(操作指南),如果没有元表,则直接返回nil,如果有元表则继续执行Step3
  • Step3:判断元表是否设置了有关索引失败的指南(__index元方法),如果没有(__indexnil),则直接返回nil;如果有__index方法是一张表,则重复执行Step1->Step2->Step3;如果__index方法是一个函数,则返回该函数的返回值

 

从Lua查找表元素的过程看元表、元方法/

Lua中rawset和rawget的作用浅析/

rawget是为了绕过__index而出现的,直接点,就是让__index方法的重写无效。(我这里用到"重写"二字,可能不太对,希望能得到纠正)

Window = {} 

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

Window.mt = {} 

function Window.new(o) 

    setmetatable(o ,Window.mt) 

    return o 

end 

 

Window.mt.__index = function (t ,key) 

    return 1000 

end 

 

Window.mt.__newindex = function (table ,key ,value) 

    if key == "wangbin" then 

        rawset(table ,"wangbin" ,"yes,i am") 

    end 

end 

 

w = Window.new{x = 10 ,y = 20} 

print(rawget(w ,w.wangbin)) 

打印结果是:nil。这里的元表中__index函数就不再起作用了。
但是rawset呢,起什么作用呢?我们再来运行一段代码。

Window = {} 

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

Window.mt = {} 

function Window.new(o) 

    setmetatable(o ,Window.mt) 

    return o 

end 

 

Window.mt.__index = function (t ,key) 

    return 1000 

end 

Window.mt.__newindex = function (table ,key ,value) 

    table.key = "yes,i am" 

end 

 

w = Window.new{x = 10 ,y = 20} 

w.wangbin = "55" 

然后我们的程序就stack overflow了。可见,程序陷入了死循环。因为w.wangbin这个元素本来就不存在表中,然后这里不断执行进入__newindex,陷入了死循环。

 Lua中rawset和rawget的作用浅析/

Lua中的异常处理pcall、xpcall、debug使用///

如果需要在Lua中处理错误,必须使用函数pcallprotected call)来包装需要执行的代码。

pcall接收一个函数和要传递个后者的参数,并执行,执行结果:有错误、无错误;返回值true或者或false, errorinfo

if pcall(function_name, ….) then

-- no error

else

-- some error

end

简单示例

> =pcall(function(i) print(i) end, 33)

33

true

> =pcall(function(i) print(i) error('error..') end, 33)

33

false        stdin:1: error..

这里注意对返回值的逻辑判断

> function f() return false,2 end

> if f() then print '1' else print '0' end

0

pcall以一种"保护模式"来调用第一个参数,因此pcall可以捕获函数执行中的任何错误。

通常在错误发生时,希望落得更多的调试信息,而不只是发生错误的位置。但pcall返回时,它已经销毁了调用桟的部分内容。Lua提供了xpcall函数,xpcall接收第二个参数——一个错误处理函数,当错误发生时,Lua会在调用桟展看(unwind)前调用错误处理函数,于是就可以在这个函数中使用debug库来获取关于错误的额外信息了。

debug库提供了两个通用的错误处理函数:

debug.debug:提供一个Lua提示符,让用户来价差错误的原因
debug.traceback:根据调用桟来构建一个扩展的错误消息

>=xpcall(function(i) print(i) error('error..') end, function() print(debug.traceback()) end, 33)

33

stack traceback:

stdin:1: in function

[C]: in function 'error'

stdin:1: in function

[C]: in function 'xpcall'

stdin:1: in main chunk

[C]: in ?

false        nil

Lua中的异常处理pcall、xpcall、debug使用///

Lua 打印Table表 //

local print = print

local tconcat = table.concat

local tinsert = table.insert

local srep = string.rep

local type = type

local pairs = pairs

local tostring = tostring

local next = next

 

function print_r(root)

         local cache = {  [root] = "." }

         local function _dump(t,space,name)

                   local temp = {}

                   for k,v in pairs(t) do

                            local key = tostring(k)

                            if cache[v] then

                                     tinsert(temp,"+" .. key .. " {" .. cache[v].."}")

                            elseif type(v) == "table" then

                                     local new_key = name .. "." .. key

                                     cache[v] = new_key

                                     tinsert(temp,"+" .. key .. _dump(v,space .. (next(t,k) and "|" or " " ).. srep(" ",#key),new_key))

                            else

                                     tinsert(temp,"+" .. key .. " [" .. tostring(v).."]")

                            end

                   end

                   return tconcat(temp,"\n"..space)

         end

         print(_dump(root, "",""))

end

 

例子:

 

a = {}

 

a.a = {

         hello = {

                   alpha = 1 ,

                   beta = 2,

         },

         world =  {

                   foo = "ooxx",

                   bar = "haha",

                   root = a,

         },

}

a.b = {

         test = a.a

}

a.c = a.a.hello

 

print_r(a)

 

 

function BetterPrintTable(lua_table)

         local cache = {  [lua_table] = "." }

         local function _dump(t, space, name)

                   local temp = {}

                   tinsert(temp, "{")

                   for k,v in pairs(t) do

                            local key = tostring(k)

                            if type(k) == "string" then

                                     key = "\"" .. tostring(k) .. "\""

                            end

                            if cache[v] then

                                     tinsert(temp, " [" .. key .. "] = {" .. cache[v] .. "}")

                            elseif type(v) == "table" then

                                     local new_key = name .. "." .. key

                                     cache[v] = new_key

                                     tinsert(temp, "\t [" .. key .. "] = " .. _dump(v, space .. (next(t,k) and " " or " " ) .. srep(" ", #key), new_key))

                            else

                                     local tempV = tostring(v)

                                     if type(v) == "string" then

                                               tempV = "\"" .. tostring(v) .. "\""

                                     end

                                     tinsert(temp, "\t [" .. key .. "] = " .. tempV)

                            end

                   end

                   tinsert(temp, "}")

                   return tconcat(temp, "\n" .. space)

         end

         print("-------------------- Better Print Table ------------------------")

         print(_dump(lua_table, "", ""))

end

 

按照lua的table格式进行缩进打印lua的table,目前还不支持key为table

function print_lua_table(lua_table, indent)

    indent = indent or 0

    for k, v in pairs(lua_table) do

        if type(k) == "string" then

            k = string.format("%q",k)

        end

        local szSuffix = ""

        if type(v) == "table" then

            szSuffix = "{"

        end

        local szPrefix = string.rep(" ", indent)

        formatting = szPrefix.."["..k.."]".." = "..szSuffix

        if type(v) == "table" then

            print(formatting)

            print_lua_table(v, indent + 1)

            print(szPrefix.."},")

        else

            local szValue = ""

            if type(v) == "string" then

                szValue = string.format("%q", v)

            else

                szValue = tostring(v)

            end

            print(formatting..szValue..",")

        end

    end

end

 

 

//

 

local raw_print = print

 

local function printTable(t)

    if type(t) == "table" then

        for _,v in pairs(t) do

            if type(v) == "table" then

                printTable(v)

            else

                raw_print(v)

            end

        end

    end

end

 

local function printExt(...)

    local args = { ... }

    for _,v in pairs(args) do

        if type(v) == "table" then

            printTable(v)

        else

            raw_print(v)

        end

    end

end

 

rawset(_G,"print",printExt)

 

print(1,2,3,{123,456,{"abc"},nil})

Lua 打印Table表 //

 

你可能感兴趣的:(UnityHotFix)