日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Lua1

 kiki的號 2017-02-14

使用lua進(jìn)行腳本編程有很多優(yōu)點(diǎn):


1 代碼體積小


2 執(zhí)行速度快


3 安全性較高等


4 但是最大的優(yōu)點(diǎn)是修改后的代碼不需要重新編譯即可生效,而高級語言的代碼經(jīng)過修改后需要經(jīng)過重新編譯或者解釋后才能生效。


 


lua主要用于游戲應(yīng)用層的開發(fā)。由于游戲上層的邏輯會而經(jīng)常發(fā)生變化,比如游戲的任務(wù)系統(tǒng)或者活動系統(tǒng)。


我們可以設(shè)想一下,如果每次都要修改我們所用的C#代碼重新編譯,那么玩家根本就無法進(jìn)行游戲。


 


lua和python則是兩個常用的腳本語言,lua相對于python而言,lua比較輕量級罷了


 


1.  腳本在手游中是類于“大腦”的功能,所有游戲相關(guān)的邏輯代碼一般都放在腳本中,而客戶端(前臺)的代碼都則屬于“肢體”,也可以說是“播放器”,


    作用只是用戶展示出UI界面的功能;那么腳本的作用那么不僅僅如此,比如地圖數(shù)據(jù)等都可以利用腳本使用;


2. 腳本在手機(jī)網(wǎng)游中的作用尤為重要,比如一款網(wǎng)游“Himi”沒有使用腳本,如果“Himi”1.0版本在發(fā)布后突然發(fā)現(xiàn)客戶端出現(xiàn)一些棘手的bug需要修復(fù),


   那么你想修改那么也要等待再次更新客戶端重新提交發(fā)布才可以解決,這樣會流失一大批用戶,而且游戲每次更新也會流失掉部分用戶,這是肯定的;


   但是如果“Himi”這款網(wǎng)游使用腳本的話,那么解決此類問題很eazy,比如我在“Himi”游戲中的邏輯代碼都放在腳本a.lua 中,那么如果a.lua邏輯中哪里出現(xiàn)了問題,


   我們直接可以將修復(fù)后的a.lua腳本更新至服務(wù)器中,因?yàn)橐话隳_本都會定義version號,比如a.lua有bug的version:1.0,那么我們修復(fù)后的a.lua version改成1.1,


   當(dāng)用戶每次啟動游戲的時候,客戶端都會將腳本的version與服務(wù)器腳本version做對比,當(dāng)server端腳本version號比當(dāng)前腳本新,那么自動下載并覆蓋當(dāng)前腳本,OK,問題解決;


   不僅僅如此,比如游戲中做個活動呀,換個圖片呀等等都可以即使更新,而不是每次修改前端代碼都要重新發(fā)布新的游戲版本,造成一些損失!


 


lua代碼都是運(yùn)行時才編譯的,不運(yùn)行的時候就如同一張圖片、一段音頻一樣,都是文件;所以更新邏輯只需要更新腳本,不需要再編譯,因而Lua能輕松實(shí)現(xiàn)“熱更新”。


Ulua是一款非常實(shí)用的unity插件,它能讓unity支持Lua語言,而且運(yùn)行效率還不錯。


 


在Lua中,一切都是變量,除了關(guān)鍵字。


lua基礎(chǔ)知識


1、and or not 邏輯運(yùn)算符


邏輯運(yùn)算符認(rèn)為false 和nil 是假(false),其他為真,0 也是true.
and 和or 的運(yùn)算結(jié)果不是true 和false,而是和它的兩個操作數(shù)相關(guān)。


Lua中的and和or都使用“短路原則”。



a and b -- 如果a 為false,則返回a,否則返回b
a or b -- 如果a 為true,則返回a,否則返回b


復(fù)制代碼

例如:
print(4 and 5) --> 5
print(nil and 13) --> nil
print(false and 13) --> false
print(4 or 5) --> 4
print(false or 5) --> 5
--一個很實(shí)用的技巧:如果x 為false 或者nil 則給x 賦初始值v
x = x or v
等價(jià)于
if not x then    --not作用:判斷某個值是否為true,【if not x : 如果x不為true】 
x = v
end
and的優(yōu)先級比or高。 

 
--C 語言中的三元運(yùn)算符
--a ? b : c
--在Lua中可以這樣實(shí)現(xiàn):
(a and b) or c   --但是這里其實(shí)有個問題,就是當(dāng)b是false或者nil時無論a是什么,最后結(jié)果都會返回c的值,用這個(a and  or {c})[1]
--not 的結(jié)果只返回false 或者true
print(not nil) --> true  --not為否定,nil為否定,兩兩否定為肯定(true)
print(not false) --> true  --not為否定,false為否定,兩兩否定為肯定(true)
print(not 0) --> false  --not為否定,0為肯定,一肯一否為否定
print(not not nil) --> false  --三個否定為否定

復(fù)制代碼

2、注釋


單行注釋中,連續(xù)兩個減號"--"表示注釋的開始


多行注釋中,由"--[["表示注釋開始,并且一直延續(xù)到"]]"為止。


3、條件語句



另外、不要在for的循環(huán)體內(nèi)修改變量i的值,否則會導(dǎo)致不可預(yù)知的結(jié)果。


for的另一種用法,是用來遍歷迭代器


復(fù)制代碼

function abc( arg )
     local i = 0
     local function cde(  )
         i = i + 1
        return i,arg[i]
     end 
     return cde
  end

for k,v in abc{'a','b','c'} do
    if v==nil then
     break
    end
    print('key = '..k..',value='..v)
end 
--key=1,value=a
--key=2,value=b
--key=3,value=c

復(fù)制代碼

foreach


復(fù)制代碼

 for k, v in ipairs(a) do  --ipairs是Lua自帶的系統(tǒng)函數(shù),返回遍歷數(shù)組的迭代器。k表示是數(shù)組a的key,v表示的是數(shù)組a的value,不能有第三個變量
        print(v)
    end
    
    for k in pairs(t) do      --打印table t中的所有key。
        print(k)
    end

復(fù)制代碼

    見如下示例代碼:


復(fù)制代碼

days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }
revDays = {}
for k, v in ipairs(days) do
    revDays[k] = v
end

for k in pairs(revDays) do
    print(k .. " = " .. revDays[k])
end

----輸出結(jié)果為:
--1 = Sunday = 7
--2 = Monday = 3
--3 = Tuesday = 4
--4 = Wednesday = 6
--5 = Thursday = 1
--6 = Friday= 5
--7 = Saturday= 2

復(fù)制代碼

4、語句塊


語句塊在C中是用"{"和"}"括起來的,在Lua中,它是用do 和 end 括起來的。比如:
do print("Hello") end
可以在 函數(shù) 中和 語句塊 中定局部變量。


5、賦值語句


Lua中的賦值語句和其它編程語言基本相同,唯一的差別是Lua支持“多重賦值”,如:a, b = 10, 2 * x,其等價(jià)于a = 10; b = 2 * x。


然而需要說明的是,Lua在賦值之前需要先計(jì)算等號右邊的表達(dá)式,在每一個表達(dá)式都得到結(jié)果之后再進(jìn)行賦值。


因此,我們可以這樣寫變量交互:x,y = y,x。如果等號右側(cè)的表達(dá)式數(shù)量少于左側(cè)變量的數(shù)量,Lua會將左側(cè)多出的變量的值置為nil,如果相反,Lua將忽略右側(cè)多出的表達(dá)式。


6、數(shù)值運(yùn)算


和C語言一樣,支持 +, -, *, /。但Lua還多了一個"^"。這表示指數(shù)乘方運(yùn)算。比如2^3 結(jié)果為8, 2^4結(jié)果為16。
連接兩個字符串,可以用".."運(yùn)處符。如:"This a " .. "string." -- 等于 "this a string"


7、比較運(yùn)算



所有這些操作符總是返回true或false。
操作符==用于相等性測試,操作符~=用于不等性測試。這兩個操作符可以應(yīng)用于任意兩個值。如果兩個值的類型不同,Lua就認(rèn)為他們不等。nil值與其自身相等。


對于字符串的比較,Lua是按照字符次序比較的。



a="123";
print(a=b);   --false
print(c==b); --true


對于Table,F(xiàn)unction和Userdata類型的數(shù)據(jù),只有 == 和 ~=可以用。相等表示兩個變量引用的是同一個數(shù)據(jù)。比如:



a={1,2}
b=a
print(a==b, a~=b)  --輸出 true, false
a={1,2}
b={1,2}
print(a==b, a~=b)  --輸出 false, true


8、變量類型



復(fù)制代碼

 > print(type("hello world"))
    string
 > print(type(10.4))
    number
 > print(type(print))
    function
 > print(type(true))
    boolean
 > print(type(nil))
    nil
 > print(type(type(X)))
    string
 > print(type({1,2,3}))
    table

復(fù)制代碼

9、 變量的定義


在Lua中還有一個特殊的規(guī)則,即以下劃線(_)開頭,后面緊隨多個大寫字母(_VERSION),這些變量一般被Lua保留并用于特殊用途,因此我們在聲明變量時需要盡量避免這樣的聲明方式,以免給后期的維護(hù)帶來不必要的麻煩。
Lua是大小寫敏感的,因此對于一些Lua保留關(guān)鍵字的使用要特別小心,如and。但是And和AND則不是Lua的保留字。


在Lua中,不管在什么地方使用變量,都不需要聲明,并且所有的這些變量總是全局變量,除非我們在前面加上"local"。這一點(diǎn)要特別注意,因?yàn)槲覀兛赡芟朐诤瘮?shù)里使用局部變量,卻忘了用local來說明。


和其它編程語言一樣,如果有可能盡量使用局部變量,以免造成全局環(huán)境的變量名污染。同時由于局部變量的有效期更短,這樣垃圾收集器可以及時對其進(jìn)行清理,從而得到更多的可用內(nèi)存。


不同lua文件之間的require的情況:如果a文件require了b文件,那么b文件里的全局變量在a文件里是能訪問到的,而局部變量是訪問不到的!


復(fù)制代碼

function fun1
  a=10;
end

function fun2
 print("a's value is "..a) -- a's value is 10
end

復(fù)制代碼

復(fù)制代碼

function fun1
  local a=10;
end

function fun2
 print("a's value is "..a) -- 報(bào)錯:attempt to concatenate global 'a' (a nil value)
end

復(fù)制代碼

(1)Nil


正如前面所說的,沒有使用過的變量的值,都是Nil。有時候我們也需要將一個變量清除,這時候,我們可以直接給變量賦以nil值。


將nil賦予一個全局變量等同于刪除它。如:



var1=nil  -- 請注意 nil 一定要小寫


另外還可以像全局變量一樣,將nil賦予table的某個元素(key)來刪除該元素。


(2)Boolean


布爾值通常是用在進(jìn)行條件判斷的時候。布爾值有兩種:true 和 false。在Lua中,只有false和nil才被計(jì)算為false,而所有任何其它類型的值,都是true。比如0,空串等等,都是true。


0在Lua中的的確確是true。你也可以直接給一個變量賦以Boolean類型的值,如:



theBoolean = true


(3)Number


Lua中的number用于表示實(shí)數(shù)。Lua中沒有專門的類型表示整數(shù)。


(4)String


           字符串,總是一種非常常用的高級類型。在Lua中,我們可以非常方便的定義很長很長的字符串。
    字符串在Lua中有幾種方法來表示,最通用的方法,是用雙引號或單引號來括起一個字符串的,如:
    "That's go!"
    或
    'Hello world!'

    和C語言相同的,它支持一些轉(zhuǎn)義字符,列表如下:
    \a  bell
    \b  back space
    \f  form feed
    \n  newline
    \r  carriage return
    \t  horizontal tab
    \v  vertical tab
    \\  backslash
    \"  double quote
    \'  single quote
    \[  left square bracket
    \]  right square bracket

    由于這種字符串只能寫在一行中,因此,不可避免的要用到轉(zhuǎn)義字符。加入了轉(zhuǎn)義字符的串,看起來實(shí)在是不敢恭維,比如:
    "one line\nnext line\n\"in quotes\", "in quotes""
    一大堆的"\"符號讓人看起來很倒胃口。如果你與我有同感,那么,我們在Lua中,可以用另一種表示方法:用"[["和"]]"將多行的字符串括起來。(lua5.1: 中括號中間可以加入若干個"="號,如 [==[ ... ]==],詳見下面示例)
    示例:下面的語句所表示的是完全相同的字符串:


復(fù)制代碼

a = 'alo\n123\''  
a = "alo\n123\""

a = [[alo
123']]

a = [==[
alo
123']==]

復(fù)制代碼

值得注意的是,在這種字符串中,如果含有單獨(dú)使用的"[["或"]]"就仍然得用"\["或"\]"來避免歧義。當(dāng)然,這種情況是極少會發(fā)生的。


在Lua中還可以通過[[ all strings ]]的方式來禁用[[ ]]中轉(zhuǎn)義字符,如:



page = [[abc\n123]] --abc\n123


如果兩個方括號中包含這樣的內(nèi)容:a = b[c[i]],這樣將會導(dǎo)致Lua的誤解析,[[a = b[c[i]]]],lua會認(rèn)為倒數(shù)第二個]]為結(jié)束標(biāo)志,因此在這種情況下,我們可以將其改為[===[ 和 ]===]的形式,從而避免了誤解析的發(fā)生。


(5)Table


A、 如果鍵是int類型的,那么鍵只能為這種格式的:[1]或者['1']


B、 如果鍵是string類型的,那么鍵只能為這種格式的:['name']或者name


關(guān)系表類型,這是一個很強(qiáng)大的類型。我們可以把這個類型看作是一個數(shù)組。只是C#語言的數(shù)組,只能用正整數(shù)來作索引;在Lua中,你可以用任意類型來作數(shù)組的索引,除了nil。


同樣,在C#語言中,數(shù)組的內(nèi)容只允許一種類型;在Lua中,你也可以用任意類型的值來作數(shù)組的內(nèi)容,除了nil。
Table的定義很簡單,它的主要特征是用"{"和"}"來括起一系列數(shù)據(jù)元素的。比如:


復(fù)制代碼

T1 = {}  -- 定義一個空表
T1[1]=10  --然后我們就可以象C#語言一樣來使用它了。

T1["John"]={Age=27, Gender="Male"}
--這一句相當(dāng)于:
T1["John"]={}  -- 必須先定義成一個表,還記得未定義的變量是nil類型嗎
T1["John"]["Age"]=27
T1["John"]["Gender"]="Male"
--當(dāng)表的索引是字符串的時候,我們可以簡寫成:
T1.John={}
T1.John.Age=27
T1.John.Gender="Male"
--或
T1.John = {Age=27, Gender="Male"}

復(fù)制代碼

在定義表的時候,我們可以把所有的數(shù)據(jù)內(nèi)容一起寫在"{"和"}"之間,這樣子是非常方便,而且很好看。比如,前面的T1的定義,我們可以這么寫:


復(fù)制代碼

     T1=
    {
      10,  -- 相當(dāng)于 [1] = 10 ,如果不寫索引,則索引就會被認(rèn)為是數(shù)字,并按順序自動從1往后編
      [100] = 40, --相當(dāng)于指定索引為100對應(yīng)的值為40 
      John=  -- 如果你原意,你還可以寫成:["John"] =
      {
        Age=27,   -- 如果你原意,你還可以寫成:["Age"] =27
        Gender=Male   -- 如果你原意,你還可以寫成:["Gender"] =Male
      },
      20  -- 相當(dāng)于 [2] = 20
    }

復(fù)制代碼

看起來很漂亮,不是嗎?我們在寫的時候,需要注意三點(diǎn):


    第一,所有元素之間,總是用逗號","隔開;
    第二,所有索引值都需要用["和"]括起來;如果是字符串,還可以去掉引號和中括號;
    第三,如果不寫索引,則索引就會被認(rèn)為是數(shù)字,并按順序自動從1往后編


表類型的構(gòu)造是如此的方便,以致于常常被人用來代替配置文件。是的,不用懷疑,它比ini文件要漂亮,并且強(qiáng)大的多。


對于table的構(gòu)造器,還有兩個需要了解的語法規(guī)則,如:
    a = { [1] = "red", [2] = "green", [3] = "blue", }  
    這里需要注意最后一個元素的后面仍然可以保留逗號(,)


table可視為數(shù)組,數(shù)組下標(biāo)不同于其他大多數(shù)語言,其下標(biāo)從1開始。


(6)Function


函數(shù),在Lua中,函數(shù)的定義也很簡單。典型的定義如下:



function add(a,b)  -- add 是函數(shù)名字,a和b是參數(shù)名字
return a+b  -- return 用來返回函數(shù)的運(yùn)行結(jié)果
end


還記得前面說過,函數(shù)也是變量類型嗎?上面的函數(shù)定義,其實(shí)相當(dāng)于:



add = function (a,b) return a+b end


當(dāng)重新給add賦值時,它就不再表示這個函數(shù)了。我們甚至可以賦給add任意數(shù)據(jù),包括nil (這樣,賦值為nil,將會把該變量清除)。


Lua的函數(shù)可以接受可變參數(shù)個數(shù),它同樣是用"..."來定義的,比如:



function sum (a,b,...)


如果想取得...所代表的參數(shù),可以在函數(shù)中訪問arg局部變量(表類型)得到 (lua5.1: 取消arg,并直接用"..."來代表可變參數(shù)了,本質(zhì)還是arg)。
如 sum(1,2,3,4),則在函數(shù)中,a = 1, b = 2, arg = {3, 4}  (lua5.1:  a = 1, b = 2, ... = {3, 4})
在含有變長參數(shù)的函數(shù)中,同樣可以帶有固定參數(shù),但是固定參數(shù)一定要在變長參數(shù)之前聲明,如:



function test(arg1,arg2,...)
        ...
end


關(guān)于Lua的變長參數(shù)最后需要說明的是,由于變長參數(shù)中可能包含nil值,因此在使用類似獲取table元素?cái)?shù)量(#)的方式獲取變參的數(shù)量就會出現(xiàn)問題。如果要想始終獲得正確的參數(shù)數(shù)量,可以使用Lua提供的select函數(shù),如:


復(fù)制代碼

function Start()  
     MyTest(2,4,6,a,b);
end

function MyTest(...)
print(select('#',...)) -- 5 , 這里'#'值表示讓select返回變參的數(shù)量(其中包括nil)。
for i = 1, select('#',...) do 
     local arg = select(i, ...) --這里的i表示獲取第i個變參,1為第一個。
     print(arg);
end
end

--輸出結(jié)果
--5 =
--2
--4
--6
--nil
--nil

復(fù)制代碼

更可貴的是,它可以同時返回多個結(jié)果,比如:



function s()
return 1,2,3,4
end
a,b,c,d = s()  -- 此時,a = 1, b = 2, c = 3, d = 4


復(fù)制代碼

function maximum(a)
    local mi = 1
    local m = a[mi]
    for i, val in ipairs(a) do
        if val > m then
            mi,m = i,val
        end
    end
    return m,mi
end
print(maximum{8,10,23,12,5})
-- 輸出結(jié)果為:
-- 23
-- 233

復(fù)制代碼

復(fù)制代碼

function abcwyq( ... )
    -- body
    return 11,22,33
end

print(abcwyq())
--輸出結(jié)果為
--11
--11 22
--11 22 33

復(fù)制代碼

前面說過,表類型可以擁有任意類型的值,包括函數(shù)!因此,有一個很強(qiáng)大的特性是,擁有函數(shù)的表,哦,我想更恰當(dāng)?shù)膽?yīng)該說是對象吧。Lua可以使用面向?qū)ο缶幊塘?。不信?舉例如下:


復(fù)制代碼

       t =
    {
     Age = 27,
     add = function(self, n) self.Age = self.Age+n end
    }
    print(t.Age)  -- 27
    t.add(t, 10)
    print(t.Age)  -- 37

復(fù)制代碼

不過,t.add(t,10) 這一句實(shí)在是有點(diǎn)土對吧?沒關(guān)系,在Lua中,我們可以簡寫成:



t:add(10)    -- 相當(dāng)于 t.add(t,10)


1、在Lua中函數(shù)的調(diào)用方式和C語言基本相同,如:print("Hello World")和a = add(x, y)。唯一的差別是,如果函數(shù)只有一個參數(shù),并且該參數(shù)的類型為字符串常量table的構(gòu)造器,那么圓括號可以省略,如print "Hello World"和f {x = 20, y = 20}。


2、Lua為面對對象式的調(diào)用也提供了一種特殊的語法--冒號操作符。表達(dá)式o.foo(o,x)的另一種寫法是o:foo(x)。冒號操作符使調(diào)用o.foo時將o隱含的作為函數(shù)的第一個參數(shù)。


3、需要說明的是,Lua中實(shí)參和形參的數(shù)量可以不一致,一旦出現(xiàn)這種情況,Lua的處理規(guī)則等同于多重賦值,即實(shí)參多于形參,多出的部分被忽略,如果相反,沒有被初始化的形參的缺省值為nil。


4、 Lua會調(diào)整一個函數(shù)的返回值數(shù)量以適應(yīng)不同的調(diào)用情況。若將函數(shù)調(diào)用作為一條單獨(dú)語句時,Lua會丟棄函數(shù)的所有返回值。若將函數(shù)作為表達(dá)式的一部分來調(diào)用時,Lua只保留函數(shù)的第一個返回值。


只有當(dāng)一個函數(shù)調(diào)用是一系列表達(dá)式中的最后一個元素時,才能獲得所有返回值。這里先給出三個樣例函數(shù),如:



function foo0() end
function foo1() return "a" end
function foo2() return "a","b" end



最后一個需要介紹的是Lua中unpack函數(shù),該函數(shù)將接收數(shù)組作為參數(shù),并從下標(biāo)1開始返回該數(shù)組(之所以不稱之為table,因?yàn)檎f成數(shù)組,更好理解,數(shù)組的特性就是從1開始,并以1為增量排序的集合)的所有元素。如:


復(fù)制代碼

> lua
> print(unpack{10,20,30})
   10  20  30
> a,b = unpack{10,20,30}
> print(a,b)
    10  20
> string.find(unpack{"hello","ll"})  --等同于string.find("hello","ll"),find函數(shù)的作用是尋找第二個參數(shù)ll在第一個參數(shù)中的索引位置

復(fù)制代碼

在Lua中unpack函數(shù)是用C語言實(shí)現(xiàn)的。為了便于理解,下面給出在Lua中通過遞歸實(shí)現(xiàn)一樣的效果,如:



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


 具名實(shí)參:


 在函數(shù)調(diào)用時,Lua的傳參規(guī)則和C語言相同,并不真正支持具名實(shí)參。但是我們可以通過table來模擬,比如:



function rename1(old,new)
        ...
end


這里我們可以讓上面的rename函數(shù)只接收一個參數(shù),即table類型的參數(shù),與此同時,該table對象將含有old和new兩個key。如:



function rename2(arg)
        local old = arg.old
        local new = arg.new
        ...
end


這種修改方式有些類似于JavaBean,即將多個參數(shù)合并為一個JavaBean。


在Lua中函數(shù)和所有其它值一樣都是匿名的,即它們都沒有名稱。在使用時都是操作持有該函數(shù)的變量,如:


    a = { p = print }
    a.p("Hello World")
    b = print
    b("Hello World")
在聲明Lua函數(shù)時,可以直接給出所謂的函數(shù)名,如:
 function foo(x) return 2 * x end
我們同樣可以使用下面這種更為簡化的方式聲明Lua中的函數(shù),如:
foo = function(x) return 2 * x end


我們將這種函數(shù)構(gòu)造式的結(jié)果稱為一個"匿名函數(shù)"。下面的示例顯示了匿名函數(shù)的方便性,它的使用方式有些類似于Java中的匿名類,如:
    table.sort(test_table,function(a,b) return (a.name > b.name) end)


復(fù)制代碼

test_table={{age=5},{age=11},{age=1},{age=3},{age=9}}
table.sort(test_table,function(a,b) return (a.age > b.age) end)
for i=1,5 do
   print(test_table[i].age)
end
end

--輸出結(jié)果
--11
--9
--5
--3
--1

復(fù)制代碼

 1. closure(閉合函數(shù)):
    若將一個函數(shù)寫在另一個函數(shù)之內(nèi),那么這個位于內(nèi)部的函數(shù)便可以訪問外部函數(shù)中的局部變量,見如下示例:


復(fù)制代碼

function newCounter() 
    local i = 0
    return function() --匿名函數(shù)
        i = i + 1
        return i
    end
end
c1 = newCounter()
print("The return value of first call is " .. c1())
print("The return value of second call is " .. c1())
--輸出結(jié)果為:
--The return value of first call is 1
--The return value of second call is 2

復(fù)制代碼

在上面的示例中,我們將newCounter()函數(shù)稱為閉包函數(shù)。其函數(shù)體內(nèi)的局部變量i被稱為"非局部變量",和普通局部變量不同的是該變量被newCounter函數(shù)體內(nèi)的匿名函數(shù)訪問并操作。


再有就是在函數(shù)newCounter返回后,其值仍然被保留并可用于下一次計(jì)算。再看一下下面的調(diào)用方式。


復(fù)制代碼

function newCounter() 
    local i = 0
    return function() --匿名函數(shù)
        i = i + 1
        return i
    end
end
c1 = newCounter()
c2 = newCounter()
print("The return value of first call with c1 is " .. c1())
print("The return value of first call with c2 is " .. c2())
print("The return value of second call with c1 is " .. c1())
--輸出結(jié)果為:
--The return value of first call with c1 is 1
--The return value of first call with c2 is 1
--The return value of second call with c1 is 2

復(fù)制代碼

 由此可以推出,Lua每次在給新的閉包變量賦值時,都會讓不同的閉包變量擁有獨(dú)立的"非局部變量"。下面的示例將給出基于閉包的更為通用性的用法:


復(fù)制代碼

do
    --這里將原有的文件打開函數(shù)賦值給"私有變量"oldOpen,該變量在塊外無法訪問。
    local oldOpen = io.open
    --新增一個匿名函數(shù),用于判斷本次文件打開操作的合法性。
    local access_OK = function(filename,mode) <檢查訪問權(quán)限> end
    --將原有的io.open函數(shù)變量指向新的函數(shù),同時在新函數(shù)中調(diào)用老函數(shù)以完成真正的打開操作。
    io.open = function(filename,mode)
        if access_OK(filename,mode) then
            return oldOpen(filename,mode)
        else
            return nil,"Access denied"
        end
    end
end

復(fù)制代碼

非全局函數(shù):


從上一小節(jié)中可以看出,Lua中的函數(shù)不僅可以直接賦值給全局變量,同時也可以賦值給其他類型的變量,如局部變量和table中的字段等。


事實(shí)上,Lua庫中大多數(shù)table都帶有函數(shù),如io.read、math.sin等。這種寫法有些類似于C++中的結(jié)構(gòu)體。如:
    Lib = {}
    Lib.add = function(x,y) return x + y end
    Lib.sub = function(x,y) return x - y end
或者是在table的構(gòu)造式中直接初始化,如:
    Lib = { add = function(x,y) return x + y end, 
               sub = function(x,y) return x - y end
             }
除此之外,Lua還提供另外一種語法來定義此類函數(shù),如:
    Lib = {}
    function Lib.add(x,y) return x + y end
    function Lib.sub(x,y) return x - y end
對于Lua中的局部函數(shù),其語義在理解上也是非常簡單的。由于Lua中都是以程序塊作為執(zhí)行單元,因此程序塊內(nèi)的局部函數(shù)在程序塊外是無法訪問的,如:



do
     local f = function(x,y) return x + y end
     --do something with f.
     f(4,5)
end  


對于這種局部函數(shù),Lua還提供另外一種更為簡潔的定義方式,如:
    local function f(x,y) return x + y end
該寫法等價(jià)于:
    local f
    f = function(x,y) return x + y end


正確的尾調(diào)用:


在Lua中支持這樣一種函數(shù)調(diào)用的優(yōu)化,即“尾調(diào)用消除”。我們可以將這種函數(shù)調(diào)用方式視為goto語句,如:
    function f(x) return g(x) end
由于g(x)函數(shù)是f(x)函數(shù)的最后一條語句,在函數(shù)g返回之后,f()函數(shù)將沒有任何指令需要被執(zhí)行,因此在函數(shù)g()返回時,可以直接返回到f()函數(shù)的調(diào)用點(diǎn)。


由此可見,Lua解釋器一旦發(fā)現(xiàn)g()函數(shù)是f()函數(shù)的尾調(diào)用,那么在調(diào)用g()時將不會產(chǎn)生因函數(shù)調(diào)用而引起的棧開銷。這里需要強(qiáng)調(diào)的是,尾調(diào)用函數(shù)一定是其調(diào)用函數(shù)的最后一條語句,否則Lua不會進(jìn)行優(yōu)化。


然而事實(shí)上,我們在很多看似是尾調(diào)用的場景中,實(shí)際上并不是真正的尾調(diào)用,如:
    function f(x) g(x) end            --沒有return語句的明確提示
    function f(x) return g(x) + 1  --在g()函數(shù)返回之后仍需執(zhí)行一次加一的指令。
    function f(x) return x or g(x) --如果g()函數(shù)返回多個值,該操作會強(qiáng)制要求g()函數(shù)只返回一個值。
    function f(x) return (g(x))     --原因同上。
在Lua中,只有"return <func>(<args>)"形式才是標(biāo)準(zhǔn)的尾調(diào)用,至于參數(shù)中(args)是否包含表達(dá)式,由于表達(dá)式的執(zhí)行是在函數(shù)調(diào)用之前完成的,因此不會影響該函數(shù)成為尾調(diào)用函數(shù)。


 


(7)Userdata 和 Thread


 10、字符串


(1)"#"標(biāo)識符,該標(biāo)識符在字符串變量的前面將返回其后字符串的長度



a = "hello"
print(#a)  --5


(2)Lua提供了運(yùn)行時的數(shù)字與字符串的自動轉(zhuǎn)換。如:
    > print("10" + 1)
    11


   > print("10" + "1")


    11


   > print(10 + 1)


    11


    > print("10 + 1")
 
   10 + 1
    如果在實(shí)際編程中,不希望兩個數(shù)字字符串被自動轉(zhuǎn)換,而是實(shí)現(xiàn)字符串之間的連接,可以通過" .. "操作符來完成。如:
  
 > print(10 .. 20)
    
1020


注意..和兩邊的數(shù)字之間必須留有空格,否則就會被Lua誤解析為小數(shù)點(diǎn)兒。


盡管Lua提供了這種自動轉(zhuǎn)換的功能,為了避免一些不可預(yù)測的行為發(fā)生,特別是因?yàn)長ua版本升級而導(dǎo)致的行為不一致現(xiàn)象。


鑒于此,還是應(yīng)該盡可能使用顯示的轉(zhuǎn)換,如字符串轉(zhuǎn)數(shù)字的函數(shù)tonumber(),或者是數(shù)字轉(zhuǎn)字符串的函數(shù)tostring()。對于前者,如果函數(shù)參數(shù)不能轉(zhuǎn)換為數(shù)字,該函數(shù)返回nil。如:


復(fù)制代碼

line = io.read()
n = tonumber(line)
if n == nil then
    error(line .. " is not a valid number")
else
    print(n * 2)
end

復(fù)制代碼

(3)由于數(shù)組實(shí)際上仍為一個table,所以對于數(shù)組大小的計(jì)算需要留意某些特殊的場景,如:   



a = {}
a[1000] = 1


在上面的示例中,數(shù)組a中索引值為1--999的元素的值均為nil。而Lua則將nil作為界定數(shù)據(jù)結(jié)尾的標(biāo)志。當(dāng)一個數(shù)組含有“空隙”時,即中間含有nil值,長度操作符#會認(rèn)為這些nil元素就是結(jié)尾標(biāo)志。


當(dāng)然這肯定不是我們想要的結(jié)果。因此對于這些含有“空隙”的數(shù)組,我們可以通過函數(shù)table.maxn()返回table的最大正數(shù)索引值。如:


復(fù)制代碼

a = {}
a[1]=20;
a["sss"]=444;
a[100]=10;
a[18]=20;
print(table.maxn(a))    -- 100
print(#a)   -- 1

復(fù)制代碼

table.maxn()是返回table的最大正數(shù)索引值


 


一般來說#是獲得一個table的長度(即元素?cái)?shù)),但這個操作符實(shí)際上陷阱很多,#僅對鍵值為連續(xù)的數(shù)值才有效,并且鍵值必須從1開始!


如果你的table是純粹當(dāng)一個連續(xù)的數(shù)組在用,那么#t是很方便的獲得table長度的方法;但如果你的table中key的數(shù)值不連續(xù),或者有其他類型的key那么還是不要指望#能給出多有意義的結(jié)果來……


連續(xù)的數(shù)組指的就是往一個空的table中插入數(shù)據(jù),由此table自動分配鍵值,由1開始,遞增量為1


類似這樣:t = {"a", "b", "c"},只有這種情況用#運(yùn)算符才最保險(xiǎn)!不過#運(yùn)算符主要用于計(jì)算字符串的長度


table遍歷:



for key, value in pairs(tbtest) do 
     XXX 
end


這樣的遍歷順序并非是tbtest中table的排列順序,而是根據(jù)tbtest中key的hash值排列的順序來遍歷的。


如果不需要保證遍歷的順序,就用pairs


pairs也可以用來遍歷連續(xù)的數(shù)組(之所以不稱之為table,因?yàn)檎f成數(shù)組,更好理解,數(shù)組的特性就是從1開始,并以1為增量排序的集合),會按順序輸出


也可以遍歷不是以1開始,也不是按1遞增的連續(xù)的key的table,也會按順序輸出的,例如:



a = {[4] = "red1",[3] = "red2",[5] = "red3",[1] = "red4"}
for k,v in pairs(a) do
    print(v)  --  red4  red2  red1  red3
end


 



for key, value in ipairs(tbtest) do 
     XXX 
end


這樣的循環(huán)必須要求tbtest中的key為順序的,而且必須是從1開始,ipairs只會從1開始按連續(xù)的key順序遍歷到key不連續(xù)為止。


如果需要保證遍歷的順序,就用ipairs,前提是table是一個連續(xù)的數(shù)組


 



for i=1, #(tbtest) do 
     XXX 
end


這種遍歷,只能遍歷連續(xù)的數(shù)組(鍵從1開始,以增量為1遞增的鍵的table),不然遍歷會出現(xiàn)意想不到的結(jié)果!


 



for i=1, table.maxn(tbtest) do 
     XXX 
end


這種效率太低了,不建議用于遍歷table


 


 


 


參考:http://www.cnblogs.com/ly4cn/archive/2006/08/04/467550.html


http://www.cnblogs.com/stephen-liu74/archive/2012/06/15/2409324.html

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多