微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Lua进阶(一)——函数闭包、元表

函数闭包


Lua中的函数是带有词法定界(lexical scoping)的第一类值(first-class values)
第一类值指:在Lua中函数和其他值(数值、字符串)一样函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。
词法定界指:被嵌套的函数可以访问他外部函数中的变量。这一特性给Lua提供了强大的编程能力。
一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。

function createCountdownTimer(second)
   local ms=second * 1000;
   local function countDown()
      ms = ms - 1;
	  return ms;
	end
	return countDown;
end

timer1 = createCountdownTimer(1);
for i=1,3 do
   print(timer1());
end
print("------------");
timer2 = createCountdownTimer(1);
for i=0,2 do
   print(timer2());
end
999
998
997
------------
999
998
997
Upvalue:一个函数使用它函数体之外的局部变量(external localvariable)称为这个函数的upvalue
在前面的代码中,函数countDown使用的定义在函数createCountdownTimer中的局部变量ms就是countDown的upvalue,但ms对createCountdownTimer而言只是一个局部变量,不是upvalue。Upvalue是Lua不同于C/C++的特有属性,需要结合代码仔细体会。

函数闭包:一个函数和它所使用的所有upvalue构成了一个函数闭包

闭包是一个内部函数,它可以访问一个或者多个外部函数的外部局部变量。每次闭包的成功调用后这些外部局部变量都保存他们的值(状态)。当然如果要创建一个闭包必须要创建其外部局部变量。所以一个典型的闭包的结构包含两个函数一个是闭包自己;另一个是工厂(创建闭包的函数)。迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,也就是他知道来自于哪里和将要前往哪里。闭包提供的机制可以很容易实现这个任务

Lua函数闭包与C函数的比较:Lua函数闭包使函数具有保持它自己的状态的能力,从这个意义上说,可以与带静态局部变量的C函数相类比。但二者有显著的不同:对Lua来说,函数是一种基本数据类型——代表一种(可执行)对象,可以有自己的状态;但是对带静态局部变量的C函数来说,它并不是C的一种数据类型,更不会产生什么对象实例,它只是一个静态地址的符号名称

基于对象的实现方式

function create(name,id)
    local data={name = name,id=id};
    local obj={};
    function obj.GetName()
      return data.name;
	end
	function obj.GetID()
	   return data.id;
	end
	function obj.SetName(name)
	   data.name=name;
	end
	function obj.SetID(id)
	   data.id=id
	end
	return obj;
end

o1 = create("Sam",001)
o2 = create("Bob",007)
o1.SetID(100)
print("o1's id:",o1.GetID(),"o2's id:",o2.GetID())
o2.SetName("Lucy")
print("o1's name:",o1.GetName(),"o2's name:",o2.GetName())

--o1's id:	100	o2's id:	7
--o1's name:	Sam	o2's name:	Lucy
实现方式:把需要隐藏的成员放在一张表里,把该表作为成员函数的upvalue。
局限性:
基于对象的实现不涉及继承及多态。但另一方面,脚本编程是否需要继承和多态要视情况而定。

元表

t = {}
m = { a = " and ",b = "Li Lei",c = "Han Meimei" }
setMetatable(t,{ __index = m}) --表{ __index=m }作为表t的元表
for k,v in pairs(t) do --穷举表t
    print(v)
end
print("-------------")
print(t.b,t.a,t.c)

--输出结果
---------------
--Li Lei	 and 	Han Meimei

function add(t1,t2)
    --‘#’运算符取表长度
    assert(#t1 == #t2)
    local length = #t1
    for i = 1,length do
    t1[i] = t1[i] + t2[i]
    end
    return t1
end
--setMetatable返回被设置的表
t1 = setMetatable({ 1,2,3},{ __add = add })
t2 = setMetatable({ 10,20,30 },{__add = add })

t1 = t1 + t2
for i = 1,#t1 do
    print(t1[i])
end
--11
--22
--33
定义:元表本身只是一个普通的表,通过特定的方法(比如setMetatable)设置到某个对象上,进而影响这个对象的行为一个对象有哪些行为受到元表影响以及这些行为按照何种方式受到影响是受Lua语言约束的。比如在前面的代码里,两个表对象的加法运算,如果没有元表的干预,就是一种错误;但是Lua规定了元表可以“重载”对象的加法运算符,因此若把定义了加法运算的元表设置到那两个表上,它们就可以做加法了。元表是Lua最关键的概念之一,内容也很丰富,请参考Lua文档了解详情。
元表与C++虚表的比较:如果把表比作对象,元表就是可以改变对象行为的“元”对象。在某种程度上,元表可以与C++的虚表做一类比。但二者还是迥然不同的:元表可以动态的改变,C++虚表是静态不变的元表可以影响表(以及其他类型的对象)的很多方面的行为,虚表主要是为了定位对象的虚方法(最多再带上一点点RTTI)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐