笔记: Lua基础: Table, Array, Namespace, Lua的面向对象

作者: apex.Cliz

table的声明及赋值

table是Lua中的hashmap(包括其实现方式也是). 由于其包含所有数组的功能, 所以可以认为table是扩展了的数组. Lua没有再额外提供单独的数组类型.

用以下的语句声明一个空的table:

newTable = {}

可以用以下的方式向table中添加值:

newTable[2] = "14"

可以使用字符串形式的key值, 但必须在字符串前后添加双引号("").

newTable["time"] = "April 14"

也可以在首次声明table的同时进行赋值, 参考下面的语句:

newTable = {
  [key1] = value1,
  [key2] = value2,
  ...
}

数组(array)在Lua中被当作table的一个特例, 即: 以1开始的连续整数作为key的table.

所以在声明时不需再指定key.

newArray = {
  value1,
  value2,
  ...
}

一个table如果包含key值为1的元素, 则该元素, 以及其后以2开始的连续的整数为key值的所有元素构成的table, 称作该table的数组部分. 一部分table的函数只会对table的数组部分产生作用.

和其他语言的数组一样, 可以通过下标获取特定的元素, 不过下标是从1而非0开始的:

> firstTable = {"alpha", "beta", "gamma"}
> print(firstTable[1])
alpha

对比: 可以用下面的方式获取字符串形式的key所对应的值:

> print(newTable.time)
April 14

可以通过#运算符获取数组的长度.

> print(#firstTable)
3

#运算符不会处理table的非数组部分.

> secondTable = {
>> "alpha",
>> "beta",
>> ["one"] = "uno",
>> ["two"] = "dos",
>> "gamma",
>> }
> print(#secondTable)
3

附注: 一个table的数组部分可以以内建的table.sort()函式进行升序排列:

> newTable = {"alpha", "gamma", "beta"}
> table.sort(newTable)
> for i = 1, #newTable do
>> print(i, newTable[i])
>> end
1, alpha
2, beta
3, gamma

关于删除:
在任何的table中, 都可以用下面的语句清空一个key所对应的值:

newTable["time"] = nil

以下语句只支持array形式的table:

value = table.remove(newTable)
value = table.remove(newTable, 2)

在删除的同时将返回被删除的key原本的值.


使用table建立namespace

table的一个元素的值可以是一段代码, 此时这个元素相当于一个函数(或方法). 换言之, 可以利用table来产生一个namespace.

回顾: 字符串形式的key所对应的值, 可以通过两种方式来获取:

> print(newTable.time)
April 14
> print(newTable["time"])
April 14

类似的, 之前所看到的table.insert(), table.remove(), table.sort()三个函式也是table表中所储存的数据, 换言之它们等价于

table["insert"]()
table["remove"]()
table["sort"]()

具体建立namespace并导入函式的方式可参考下列代码:

> util = {}
> function convert(celsius)
>> return ((celsius * 1.8) + 32)
>> end
> util.convert = convert
> print(convert(0))
32
> print(util.convert(0))
32

或者, 更直接地:

> function util.convert(celsius)
>> return ((celsius * 1.8) + 32)
>> end
> print(util.convert(0))
32


通过table实现OOP

通过上述的table功能, 可以在Lua中方便地实现面向对象编程.

首先, 用一般的table声明方式先声明"private data"

counter = {
  count = 0
}

接下来再追加其中的方法:

function counter.get(self)
  return self.count
end

function counter.inc(self)
  self.count = self.count + 1
end

这样就构成了一个计数器物体, 有一个计数用的变量(count)以及两个计数相关的方法, 可以这样使用:

> print(counter.get(counter))
0
> counter.inc(counter)
> print(counter.get(counter))
1

可以再产生一个相同类型的对象, 需要重复书写一部分代码; 函式可以不用重新定义.

> counter2 = {
 count = 15,
 get = counter.get,
 inc = counter.inc,
}
> print(counter2.get(counter2))
15

这种定义方式的好处是免除了在其他OOP语言当中经常浪费程序编写人员精力的疑问, 例如令人头大的访问权限(private, public, protected之类), 以及 "我用一个=运算符copy了一个方法, 我到底是copy了方法的内容、指针, 或者只是把新方法指向了当前的方法" 等等疑问.

上述方式的一个麻烦是, 在呼叫table自身变量(例如count)的时候, 必须再将table名输入一次(例如counter.get(counter)输入了两次counter). 这样的方式, 在疏忽下可能产生混乱(如: counter2.get(counter)). 一种可行的改进方法是使用":"代替"."来呼叫object method.

在Lua中, 以下的两种定义函式的方式是等价的:

function tbl:MyMethod() end
function tbl.MyMethod(self) end

并且, 这里的self就是拥有此method的table. 看下面的代码:

counter = {
 count = 0
}

function counter:get()
 return self.count
end

function counter:inc()
 self.count = self.count + 1
end

代码被进一步地优化了.

利用:的定义方式, 可以把之前的OOP语句 改造得和其他OOP语言更加接近.

do
 local function get(self)
  return self.count
 end

 local function inc(self)
  self.count = self.count + 1
 end

 function new_counter(value) -- 注意这个function不是local的. 这是个建构器
  if type(value) ~= "number" then
   value = 0
  end

  local obj = {
   count = value,
   get = get,
   inc = inc,
  }
  return obj
 end
end

> counter = new_counter()
> print(counter:get())
0
> counter2 = new_counter(15)
> print(counter2:get())
15
> counter:inc()
> print(counter:get())
1
> print(counter2:get())
15

这样的代码看起来和C++/Java有微妙的相似感.

Tag标签: Lua

你可能感兴趣的:(function,HashMap,table,oop,lua,语言)