您好,欢迎来到测品娱乐。
搜索
您的当前位置:首页Lua中的closure(闭合函数)

Lua中的closure(闭合函数)

来源:测品娱乐
Lua中的closure(闭合函数)

词法域:若将⼀个函数写在另⼀个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,这项特征称之为“词法域”。例:假设有⼀个学⽣姓名的列表和⼀个对应于没个姓名的年级列表,需要根据每个学⽣的年级来对他们的姓名进⾏排序(由⾼到低)。可以这么做:

names = {\"Peter\", \"Paul\", \"Mary\"}

grades = {Mary = 10, Paul = 7, Peter = 8}table.sort(names, function (n1, n2)

return grades[n1] > grades[n2] -- ⽐较年级 end)

现在假设单独创建⼀个函数来做这项⼯作:

function sortbygrade (names, grades) table.sort(names, function (n1, n2)

return grades[n1] > grades[n2] -- ⽐较年级 end)end

上例中有⼀点很有趣,传递给sort的匿名函数可以访问参数grades,⽽grades是外部函数sortbygrade的局部变量。在这个匿名函数内部,grades既不是全局变量也不是局部变量,将其称为⼀个“⾮局部的变量(non-local variable)”。为什么在Lua中允许这种访问呢?运因在与函数是“第⼀类值”。考虑⼀下代码:

function newCounter() local i = 0

return function () -- 匿名函数 i = i + 1 return i endend

c1 = newCounter()print(c1()) --> 1print(c1()) --> 2

在这段代码中,匿名函数访问了⼀个“⾮局部的变量”i,改变两⽤于保持⼀个计数器。出刊上去,由于创建变量i的函数(newCounter)已经返回,所以之后每次调⽤匿名函数时,i都应该是已超出作⽤范围的。但其实不然,Lua会以closure的概念来正确地处理这种情况。简单地说,⼀个closure就是⼀个函数加上该函数所需访问的所有“⾮局部的变量”。如果再次调⽤newCounter,那么它会创建⼀个新的局部变量i,从⽽也将得到⼀个新的closure:

c2 = newCOunter()print(c2()) --> 1print(c1()) --> 3print(c2()) --> 2

因此c1和c2是同⼀个函数所创建的两个不同的closure,它们各⾃拥有局部变量i的独⽴实例。

从技术上讲,Lua中只有closure,⽽不存在“函数”。因为,函数本⾝就是⼀种特殊的closure。不过只要不会引起混淆,仍将采⽤属于“函数”来指代closure。

在很多场合中closure都是⼀种很有价值的⼯具。就像只前所看到的,它们可作为sort这类⾼阶函数的参数。closure对于那些创建其他函数的函数也很有价值,例如前例中的newCounter。这种机制使Lua程序可以混合那些在函数式百年成世界中久经考验的编程技术。另

外,closure对于回调函数也很有⽤。这⾥有⼀个典型的例⼦,假设有⼀个传统的GUI⼯具包可以创建按钮,每个按钮都有⼀个回调函数,每当⽤户按下按钮时GUI⼯具包都会调⽤这些回调函数。再假设,基于此要做⼀个⼗进制计算器,其中需要10个数字按钮。会发现这些按钮之间的区别其实并不⼤,仅需在按下不同按钮时做⼀些稍微不同的操作就可以了。那么可以使⽤以下函数来创建这些按钮:

function digitButton (digit)

return Button{ label = tostring(digit), action = function ()

add_to_display(digit) end }end

closure在另⼀种情况中也⾮常有⽤。例如在Lua中函数是存储在普通变量中的,因此可以轻易地重新定义某些函数,甚⾄是重新定义那些预定以的函数。这正是Lua相当灵活的原因之⼀。通常当重新定义⼀个函数的时候,需要在新的视线中调⽤原来的那个函数。举例来说,假设要重新定义函数sin,使其参数能使⽤⾓度来替换原先的弧度。那么这个⼼寒数就必须得转换他的实参,并调⽤原来的sin函数完成真正的计算。这段代码可能是这样的:

oldSin = math.sin

math.sin = function (x)

return oldSin(x*math.pi/180)end

还有⼀种更彻底的做法是这样的:

do

local oldSin = math.sin local k = math.pi/180 math.sin = function (x) return oldSin(x*k) endend

将⽼版本的sin保存到了⼀个私有变量中,现在只有通过新版本的sin才能访问它了。

可以使⽤同样的技术来创建⼀个安全地运⾏环境,即所谓的“沙盒(sandbox)”。当执⾏⼀些未受信任的代码时就需要⼀个安全地运⾏环境,例如在服务器中执⾏那些从Internet上接收到的代码。举例来说,如果要⼀个程序访问⽂件的话,只需使⽤closure来重定义函数io.open就可以了。

do

local oldOpen = io.open

local access_OK = function (filename, mode) <检查访问权限> end

io.open = function (filename, mode) if access_OK(filename, mode) then return oldOpen(filename, mode) else

return nil, \"access denied\" end endend

这个⽰例的精彩之处在于,经过重新定义后,⼀个程序就只能呢该通过新的受限版本来调⽤原来哪个未受限的open函数了。⽰例将原来不安全的版本保存到closure的⼀个私有变量中,从⽽使得外部再也⽆法直接访问到原来的版本了。通过这种技术,可以在Lua的语⾔层⾯上就构建除⼀个安全地运⾏环境,且不是简易性了灵活性。相对于提供⼀套⼤⽽全的解决⽅案,Lua提供的则是⼀套“元机制(meta-mechanism)”,因此可以根据特定的安全需要来创建⼀个安全的运⾏环境。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- cepb.cn 版权所有 湘ICP备2022005869号-7

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务