天天看點

Lua基礎(轉)

局部定義與代碼塊:

  使用local聲明一個局部變量或局部函數,局部對象隻在被聲明的那個代碼塊中有效。

  代碼塊:一個控制結構、一個函數體、一個chunk(一個檔案或文本串)(Lua把chunk當做函數處理)

  這樣,可以在chunk内部聲明局部函數,該函數僅在chunk内可見,并且詞法定界保證了包内其他函數可以調用此函數。  

    在chunk内部定義多個local function并且互相調用(或定義local遞歸調用函數)時,最好先聲明,再定義。

  應該盡可能的使用局部變量(使用關鍵詞local聲明的變量),有兩個好處:

  1. 避免命名沖突

  2. 通路局部變量的速度比全局變量更快.

  可以用do..end來顯示的控制local的作用範圍,Lua中的do...end就相當于C++中的{},它定義了一個作用域。

多傳回值函數:

  第一,當作為表達式調用函數時,有以下幾種情況:

  1. 當調用作為表達式最後一個參數或者僅有一個參數時,根據變量個數函數盡可能多地傳回多個值,不足補nil,超出舍去。

  2. 其他情況下,函數調用僅傳回第一個值(如果沒有傳回值為nil)

  第二,函數調用作為函數參數被調用時,和多值指派是相同。

  第三,函數調用在表構造函數中初始化時,和多值指派時相同。

  另外,return f()這種形式,則傳回“f()的傳回值”:

  可以使用圓括号強制使調用傳回一個值。

  unpack:函數多值傳回的特殊函數,接受一個數組作為輸入參數,傳回數組的所有元素。

  函數可變參數:...表示可變參數,函數體中用arg通路,同時arg還有一個域n表示參數的個數。

  多值指派經常用來交換變量,或将函數調用傳回給變量:

調用函數的時候,如果參數清單為空,必須使用()表明是函數調用。

上述規則有一個例外,當函數隻有一個參數并且這個參數是字元串或者表構造的時候,()可有可無。

Lua使用的函數,既可是Lua編寫的,也可以是其他語言編寫的,對于Lua程式員,用什麼語言實作的函數使用起來都一樣。(優勢就在此處)

Lua中的函數是帶有詞法定界(lexical scoping)的第一類值(first-class values)。

  第一類值指:在Lua中函數和其他值(數值、字元串)一樣,函數可以被存放在變量中,也可以存放在表中,可以作為函數的參數,還可以作為函數的傳回值。

  詞法定界指:嵌套的函數可以通路他外部函數中的變量。這一特性給Lua提供了強大的程式設計能力。

  函數是第一類值(first-class values),也就是說函數名字比如print,實際上是一個指向函數的變量,像持有其他類型值的變量一樣。

  進階函數(higher-order function):以其他函數作為參數的函數,其實和普通函數沒啥差別,隻是參數類型是一個函數而已。

  将第一類值函數應用在表中是Lua實作面向對象和包機制的關鍵。

  函數的一般化定義應該是這樣的:

  foo = function (x) return 2*x end

  而

  function foo (x) return 2*x end

  隻是函數定義的一個特例。

    Lua中的函數也可以沒有名字,也就是匿名函數。

閉包:閉包就是一個函數以及它的upvalues;閉包就是将局部變量和一個函數進行打包維護。

  因為Lua函數是帶有詞法界定的第一類值,閉包才得以實作,而且閉包是一個強大的功能。

  upvalues:外部的局部變量(external local variable),也就是被内嵌的函數可以通路的外部那個函數中的變量。這些外部函數的局部變量和内嵌的函數就形成了一個閉包。

疊代器:Lua中疊代器是一個用閉包實作的函數。 

尾調用(Tail Calls):當函數的最後一個動作是調用一個函數時,我們稱這種調用為尾調用(明确什麼才是尾調用,類似return func(...)這種格式的就是尾調用)。

  尾調用不需要再回到調用者函數中,是以不适用額外的棧,這一點很重要,因為正确的尾調用是可以無限制的,并且不會導緻棧溢出。

  尤其是在寫遞歸邏輯時,不要寫成嵌套調用的形式(會不停的壓棧),而建議用尾調用的方式來實作,這樣效率會高很多。

Lua編譯與運作:

  Lua是解釋性語言,但Lua會首先把代碼預編譯成中間碼然後再執行。不要以為需要編譯就不是解釋型語言,Lua的編譯器是語言運作時的一部分,是以,執行編譯産生中間碼速度會更快。

  dofile/dostring和loadfile/loadstring的差別:

  (1)do*會編譯并執行;load*隻編譯代碼生成中間碼并且傳回編譯後的chunk作為一個函數,但不執行代碼。

  (2)load*較為靈活,發生錯誤時load*會傳回nil和錯誤資訊(可以列印出來)。

  (3)如果要運作一個檔案多次,load*隻需要編譯一次,但可以多次運作,do*每次都需要編譯。

  (4)dostring(str)等價于loadstring(str)()

  Lua把chunk作為匿名函數處理,例如:chunk "a = 1",loadstring傳回與其等價的function () a = 1 end

  loadfile和loadstring隻是編譯chunk成為自己内部實作的一個匿名函數,但是這個過程沒有定義函數的行為。Lua中的函數定義是發生在運作時的指派而不是發生在編譯時。也就是說loadstring以後,其中的函數還沒有被定義,而dostring以後函數就定義好并且可以調用了。

  loadstring編譯的時候不關心詞法範圍,也就是說loadstring總是在全局環境中編譯他的串,這一點很重要。

  local i = 0

  f = loadstring("i = i + 1") --使用全局變量i

  g = function () i = i + 1 end --使用局部變量i

  注意:chunks内部可以定義局部變量也可以傳回值:

  利用asset擷取更多的錯誤資訊是個好習慣。

Lua中的錯誤與異常:

  Lua中error的處理:Lua經常作為擴充語言嵌入在别的應用中,是以不能當錯誤發生時簡單的崩潰或者退出。相反,當錯誤發生時Lua結束目前的chunk并傳回到應用中。

  當Lua遇到不期望的情況時就會抛出錯誤,你也可以通過調用error函數顯式地抛出錯誤。

  當函數遇到異常有兩個基本的動作:傳回錯誤代碼或者抛出錯誤。選擇哪一種方式,沒有固定的規則,不過基本的原則是:對于程式邏輯上能夠避免的異常,以抛出錯誤 

的方式處理之,否則傳回錯誤代碼。

  error函數:顯示的抛出一個錯誤,終止正在執行的函數,并傳回錯誤資訊

  assert函數:如果表達式出現錯誤,則觸發一個錯誤,傳回出錯資訊

  assert首先檢查第一個參數,若沒問題,assert不做任何事情;否則,assert以第二個參數作為錯誤資訊抛出。第二個參數是可選的。注意,assert會首先處理兩個參數,

然後才調用函數,是以下面代碼,無論n是否為數字,字元串連接配接操作總會執行:

  n = io.read()

  assert(tonumber(n), "invalid input: " .. n .. " is not a number")

  pcall函數:在保護模式下調用函數(即發生的錯誤将不會反射給調用者),當調用函數成功能傳回true,失敗時将傳回false加錯誤資訊。pcall不會終止函數的繼續運作。我們通過error抛出異常,然後通過pcall捕獲之。這種機制提供了強大的能力,足以應付Lua中的各種異常和錯誤情況。pcall傳回錯誤資訊時,已經釋放了儲存錯誤發生情況的棧資訊。

  if pcall(foo) then  --foo是一個函數,pcall(foo)表示調用此函數

  ...  -- no errors while running `foo'

  else

  ...  -- `foo' raised an error: take appropriate actions

  end

    如果遇到内部錯誤(比如對一個非table的值使用索引下标通路)Lua将自己産生錯誤資訊,否則Lua使用傳遞給error函數的參數作為錯誤資訊。

  xpcall函數:xpcall接受兩個參數:調用函數、錯誤處理函數。當錯誤發生時,Lua會在棧釋放以前調用錯誤處理函數,是以可以使用debug庫收集錯誤相關資訊。

  debug.debug函數:debug處理函數,可以自己動手察看錯誤發生時的情況。

  debug.trackback函數:debug處理函數,過traceback建立更多的錯誤資訊,也是控制台解釋器用來建構錯誤資訊的函數。

 動态連結庫:

  通常Lua不包含任何不能用标準C實作的機制,動态連結庫是一個特例(動态連結庫不是ANSI C的一部分,也就是說在标準C中試線動态連結是很困難的)。

  我們可以将動态連接配接庫機制視為其他機制之母:一旦我們擁有了動态連接配接機制,我們就可以動态的加載Lua中不存在的機制。

  loadlib函數提供了Lua中動态連結庫的功能。

  local path = "/usr/local/lua/lib/libluasocket.so"

  local f = assert(loadlib(path, "luaopen_socket"))

資料結構:

  table是Lua中唯一的資料結構,可以通過table來實作其他常用的資料結構。

  數組:table下标通路就是數組。習慣上,Lua下标從1開始,Lua的标準庫都遵循此慣例,是以自定義的數組下标也最好從1 開始,這樣才能使用标準庫函數。

  矩陣和多元數組:table天生具有稀疏的特性,在表示矩陣時可以節省很多空間,因為那些值為nil的元素不需要存儲。

  連結清單:lua中很少會使用這種結構,因為用其他的結構都可以替代。連結清單的簡單實作如下所示:

Lua基礎(轉)
Lua基礎(轉)

  隊列和雙向隊列:雖然可以使用Lua的table庫提供的insert和remove操作來實作隊列,但這種方式實作的隊列針對大資料量時效率太低,有效的方式是使用兩個索引下标,一個表示第一個元素,另一個表示最後一個元素。

Lua基礎(轉)
Lua基礎(轉)

  集合和包:Lua中表示集合有一個簡單有效的方法,将所有集合中的元素作為下标存放在一個table裡,對于給定的元素,測試該表的對應下标的元素值是否為nil,即可知道該元素是否存在。

  table.concat可以将一個清單的所有串合并。

  字元串合并操作,會産生指派字元串操作,用的時候需要注意。比如str1..str2,會道指建立一個新的字元串,并将str1 和str2都拷貝過去,如果字元串很大,是比較費的操作。

 Packages:

  Lua并沒有提供明确的機制來實作packages。然而,我們通過語言提供的基本的機制很容易實作他。主要的思想是:像标準庫一樣,使用表來描述package。

  package和真正地命名空間的差別:首先,我們對每一個函數定義都必須顯示的在前面加上包的名稱。第二,同一包内的函數互相調用必須在被調用函數前指定包名。

  在包定義結尾加上一個return語句:package打開的時候傳回本身是一個很好的習慣。額外的傳回語句并不會花費什麼代價,并且提供了另一種操作package的可選方式。

  私有成員:在Lua中一個傳統的方法是将私有部分定義為局部變量來實作。我們野可以将package内的所有函數都聲明為局部的,最後将他們放在最終的表中。但是注意:一個package的私有成員必須限制在一個檔案之内,我認為這是一件好事。

  自動加載:隻在需要的時候加載對應的函數

table庫:

  table标準庫提供了一些友善的函數,用來處理比如數組大小、插入/删除元素、排序等。其中有一些需要注意的點:

  (1)一個常見的錯誤是企圖對表的下标域進行排序。在一個表中,所有下标組成一個集合,但是無序的。如果你想對他們排序,必須将他們複制到一個array然後對這個array排序。

  (2)對于Lua來說,數組是無序的。但是我們知道怎樣去計數,是以隻要我們使用排序好的下标通路數組就可以得到排好序的函數名。這就是為什麼我們一直使用ipairs而不是pairs周遊數組的原因。前者使用key的順序1、2、……,後者表的自然存儲順序。

  (3)table.sort這個函數隻有table是數組的時候才适用。

  (4)table的instert和remove函數,如果不帶位置參數,都表示操作array的最後一個元素(這樣不會移動元素)。

  (5)setn函數已過時,不要在lua的table中使用nil值,如果一個元素要删除,直接remove,不要用nil去代替。

string庫:

  Lua中的字元串是恒定不變的。String.sub函數以及Lua中其他的字元串操作函數都不會改變字元串的值,而是傳回一個新的字元串。

  Lua中,字元串的第一個字元索引從1開始。

  string.sub(s,i,j)函數可以使用負索引,負索引從字元串的結尾向前計數:-1指向最後一個字元,-2指向倒數第二個,以此類推。第3個參數,預設為-1。

  string.len(s)傳回字元串s的長度;

  string.rep(s, n)傳回重複n次字元串s的串;

  string.lower(s)将s中的大寫字母轉換成小寫(string.upper将小寫轉換成大寫);

  string.char函數和string.byte函數用來将字元在字元和數字之間轉換;

  string.format在用來對字元串進行格式化的時候,特别是字元串輸出,是功能強大的工具;

  string.find(字元串查找);

  string.gsub(全局字元串替換);

  string.gfind(全局字元串查找)。

  特殊字元:在模式比對中有一些特殊字元,他們有特殊的意義,Lua中的特殊字元如下:( ) . % + - * ? [ ^ $

  '%' 用作特殊字元的轉義字元,轉義字元 '%'不僅可以用來轉義特殊字元,還可以用于所有的非字母的字元。當對一個字元有疑問的時候,為安全起見請使用轉義字元轉義他。隻有字元串被用作模式串用于函數的時候,'%' 才作為轉義字元。

  []方括号表示比對字元集

  -中橫線連接配接符

  ^表示補集

  Lua的字元類依賴于本地環境

  模式修飾符:Lua中的模式修飾符有四個:

  + 比對前一字元1次或多次 最長比對

  * 比對前一字元0次或多次

  - 比對前一字元0次或多次 最短比對

  ? 比對前一字元0次或1次

  Capture是這樣一種機制:可以使用模式串的一部分比對目标串的一部分。将你想捕獲的模式用圓括号括起來,就指定了一個capture。

IO庫:

  I/O操作兩種模式:

  (1)簡單模式:目前輸入檔案、目前輸出檔案,使用io.input和io.output來設定;

  (2)完全模式:使用檔案句柄,它以一種面對對象的形式,将所有的檔案操作定義為檔案句柄的方法。

  IO庫的所有函數都放在表io中。

  I/O庫将目前輸入檔案作為标準輸入(stdin),将目前輸出檔案作為标準輸出(stdout)。

  io.read函數:從目前檔案讀入串,用參數來設定讀取的模式:

  (1)io.read("*all")函數從目前位置讀取整個輸入檔案,注意是從目前位置開始讀入。

  (2)io.read("*line")函數傳回目前輸入檔案的下一行(不包含最後的換行符)。

  (3)io.read("*number")函數從目前輸入檔案中讀取出一個數值。隻有在該參數下read函數才傳回數值,而不是字元串。可以這樣使用,一次讀取多個數字:

  local n1, n2, n3 = io.read("*number", "*number", "*number")

  (4)io.read(n)讀取num個字元到串,io.read(0)函數的可以用來測試是否到達了檔案末尾。如果不是傳回一個空串,如果已是檔案末尾傳回nil。

  由于Lua對長串類型值的有效管理,在Lua中使用過濾器的簡單方法就是讀取整個檔案到串中去,處理完之後(例如使用函數gsub),接着寫到輸出中去。在任何情況下,都應該考慮選擇使用io.read函數的 " *.all " 選項讀取整個檔案,然後使用gfind函數來分解。

  通常Lua中讀取整個檔案要比一行一行的讀取一個檔案快的多。

  盡量使用第二種方法,它避免了串聯操作,消耗較少的資源

  io.write(a..b..b)

  io.write(a, b, c)

  write和print的不同之處:

  (1)write不附加任何額外的字元到輸出中去;

  (2)write函數是使用目前輸出檔案,而print始終使用标準輸出;

  (3)print函數會自動調用參數的tostring方法,可以顯示出table、function和nil。

  将我們的資料檔案内容作為Lua代碼寫到Lua程式中去。通過使用table構造器,這些存放在Lua代碼中的資料可以像其他普通的檔案一樣看起來引人注目。

  Lua構造器,自描述資料格式,資料描述是Lua的主要應用之一。

http://www.cnblogs.com/sifenkesi/p/3757282.html

繼續閱讀