美文网首页Lua
Lua 学习笔记

Lua 学习笔记

作者: WuZhouIsHere | 来源:发表于2017-01-25 15:24 被阅读117次

    最近要用到 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 程序设计

    相关文章

      网友评论

        本文标题:Lua 学习笔记

        本文链接:https://www.haomeiwen.com/subject/efvebttx.html