首页 技术 正文
技术 2022年11月14日
0 收藏 563 点赞 5,115 浏览 2454 个字

词法域:若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,这项特征称之为“词法域”。

例:假设有一个学生姓名的列表和一个对应于没个姓名的年级列表,需要根据每个学生的年级来对他们的姓名进行排序(由高到低)。可以这么做:

names = {"Peter", "Paul", "Mary"}
grades = {Mary = , Paul = , Peter = }
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 =
return function () -- 匿名函数
i = i +
return i
end
endc1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2

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

c2 = newCOunter()
print(c2()) --> 1
print(c1()) --> 3
print(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/)
end

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

do
local oldSin = math.sin
local k = math.pi/
math.sin = function (x)
return oldSin(x*k)
end
end

将老版本的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
end
end

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

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,489
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,904
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,737
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,490
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,128
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,291