Lua 學習筆記

最近要用到 Lua 編程語言,所以學習了一些簡明教程,同時記錄一下 Lua 編程語言相對于其他主流編程語言在語法上特殊的地方。其中,在 Lua 中使用Table數據結構實現“面向對象”編程是重點。

注釋

-- 單行注釋
--[[
塊注釋,有趣的是這個注釋標記不是對稱的
--]]

變量

  1. 變量沒有類型,值才有類型,也就是在聲明變量的時候不需要聲明變量的類型。
  2. 數字只有 double 類型 。
  3. 沒有定義過的變量值為 nil
  4. 對于布爾類型,只有 nilfalse 表示假,其他的值都為真。
  5. 默認變量都是全局變量,局部變量需要加 local 關鍵字

操作符

  1. Lua 中沒有 +++= 這類的運算符
  2. 不等于號是 ~=
  3. 字符串鏈接符是 ..
  4. 條件表達的“與”、“或”、“非”分別是and,or,not

控制語句

if-else 分支

i = 10
if i = 0 then
-- do something
elseif i > 5 and i < 10 then
-- do something
else
-- do something 
end

while 循環

i = 0
while i < 100 do
  -- do something
  i = i + 1
end

until 循環

sum = 2
repeat
  sum = sum ^ 2
until sum > 100

for 循環

for i=1, 100, 2 do
-- do something
end

函數

函數可以作為返回值返回

function add(x)
  return function(y) y + x end
end

addOne = add(1)
addOne(1) -- 2

可以返回多個值,同時默認為全局函數。

值得注意的是 Lua 的函數參數不支持默認值,這也為 Lua 程序的重構帶來了一些麻煩。

Table

Table 是 Lua 中重要的數據結構,它是由一系列的 key-value 鍵值對組成。Lua 中的數組也是一種特殊的 Table它下標從 1 開始,而且在一個數組中可以有不同類型的成員。

變量對于 Table 的引用是弱引用,也就是說 Table 是獨立于變量存在的,只有當沒有任何一個變量引用這個 Table 的時候,Lua 的垃圾回收機制才會把這個 Table 從內存中回收。這個特性對于在 Lua 中實現“面向對象”也很重要。

-- 定義和訪問
person = {name = "jack", age =24}
person.name = "black"
person.age = 20

-- 另一種定義和訪問
person2 = {['name']="green", ['age']=30}
person2['name'] = "tom"
person2['age'] = 10

-- 數組
arr = {1,2,3,4,5}
arr = {[1]=1,[2]=2,[3]=3,[4]=4,[5]=5} -- 與上面的定義等價

MetaTable 和 MetaMethod

Lua 中每個值都有一套預定義的操作集合,這個集合就是 MateTable ,MetaTable 中預定義的方法就是 MetaMethod。tableuserdata 有各自獨立的 MetaTable,從而可以利用這一特性實現“面向對象”編程。而其他類型的值則共享屬于該類型的一個 MetaTable。

面向對象

Lua 可以通過使用 Table 這種數據結構來實現面向對象編程。但是這種面向對象并不是基于類(Class)的,而是基于原型(prototype)的。這個原型就是我們定義好的一個 Table,其他對象就可以通過這個原型衍生出來。

定義原型

首先定義一個 Table 當作我們的原型,并定義一個成員變量

Account = { balance = 0 }

這樣原型就定義好了,由于 Table 是獨立于變量存在的, 只要不把 Account 變量設為 nil ,那么就這個原型就一直存在。在這個原型中有一個 balance 的成員變量。

定義成員方法

緊接上面的代碼,我們可以定義一個成員方法

function Account.withdraw(self, v)
  self.balance = self.balance - v
end

其中 Account.withdraw 是一個語法糖, 相當于在 Account 的 Table 中定義了一個 withdraw 的字段,而它的內容就是一個方法,上面的寫法等價于:

Account = {
  withdraw = function (self, v)
    self.balance = self.balance - v
  end 
}

在這個方法中使用了 self 關鍵字,它就是指這個 Table 本身,它有可能是這個原型,也可能是由這個原型衍生出的對象。

同時我們可以在定義成員方法時使用 : 語法糖,默認傳入 self 提高編碼效率。例如:

function Account:withdraw(v)
 self.balance = self.balance - v
end

生成對象

對象其實也是一個 Table , 只不過這個 Table 并不是空的,而是基于一個原型產生的。基于原型的 Table 就是利用上面的 MetaTable 來實現的。同樣基于上面的代碼實現一個產生基于 Account 原型的對象的方法new

function Account:new(o)
  if o == nil then
    o = {}
  end
  
  setmetatable(o, self) -- 綁定原型

  self.__index = self --索引原型,方便使用點號

  return o
end

這個方法的關鍵就是在對象的 MetaTable 中添加了原型 Account。這樣當我們要訪問對象 o 這個中的某個未定義的字段時,Lua 就會去查找它的 MetaTable 中是否有這個字段,由于我們綁定了原型,這樣就能找到原型中的這個字段。比如,我們并沒有為 o 定義 withdraw 方法,所以當我們訪問 o 的 withdraw 字段時,就會發現 o 本身的 Table 并沒有這個字段,然后去查找它的 MetaTable,這樣就能訪問到原型,也就是 Accountwithdraw 的實現。

派生

由原有的原型派生出新的原型在 Lua 中實現起來也不難,因為原型也是 Table, 我們只要為這個 Table 添加新的字段,或者為原有字段定義新的內容就行了,例如:

SpecialAccount = Account:new{limit = 100}

-- 重新定義 withdraw 方法
function SpecialAccount:withdraw(v)
  if v < self.limit then
    self.balance = self.balance - v
  end
end

在上面的代碼中就從 Account 原型中派生出了一個新的原型,其中添加了一個新的limit成員變量和重新定義了withdraw成員方法。

參考資料

  1. LUA簡明教程
  2. Lua 5.1 參考手冊
  3. Lua 程序設計
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1.1程序塊:Lua執行的每段代碼,例如一個源代碼文件或者交互模式中輸入的一行代碼,都稱為一個程序塊 1.2注釋:...
    c_xiaoqiang閱讀 2,619評論 0 9
  • 1、loadstring、dofile和loadfile的用法和區別: (1)對loadstring,程序示例如下...
    陳振擁閱讀 517評論 0 2
  • lua C api PS:這里是默認我已經學完了lua腳本的基本知識(包括table,元表,函數,基本庫, 文件i...
    綠箭ML閱讀 3,480評論 0 1
  • 最近項目要用的lua 所以抽空學習 記錄一下 IDE有ZeroBrane Studio 我還是用的sublime...
    slimsallen閱讀 657評論 3 1
  • 換了一種紙,有點把控不了的感覺!
    夏又幽閱讀 186評論 0 0