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

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

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
end c1 = 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)”,因此可以根据特定的安全需要来创建一个安全的运行环境。

最新文章

  1. 第三周作业(一):安装VS以及创建单元测试
  2. mongoosejs学习地址
  3. Product Backlog
  4. LR自定义函数以及调用
  5. iOS中JS 与OC的交互(JavaScriptCore.framework)
  6. spring-boot资料
  7. 在WINDOWS SERVER 上或远程桌面中使用 MUTEX
  8. 如何查看IIS并发连接数
  9. jQuery基础学习2——DOM和jQuery对象
  10. 缓存(之一) 使用Apache Httpd实现http缓存
  11. 浅谈angular框架
  12. JS多语种方式
  13. RegExp对象的三个方法
  14. 解决 Cannot find OpenSSL&#39;s &lt;evp.h&gt; 和sasl.h not found!
  15. IT技术团队管理之成长
  16. SlidingMenu的使用详解
  17. sqlite3使用总结(转并且修改)
  18. buildroot构建项目(八)--- u-boot 2017.11 适配开发板修改 5 ---- 系统启动初始化之五
  19. HR别掉坑里了,送你最精确的计薪算法!
  20. 最小二乘法 及 梯度下降法 分别对存在多重共线性数据集 进行线性回归 (Python版)

热门文章

  1. Spring JDBC ResultSetExtractor接口示例
  2. e870. 获取默认外观的数据
  3. Tomcat 部署一工程时Deploy Location 为什么 是 INVALID
  4. (转)Live555中RTSPClient分析
  5. AOP (面向切面编程)
  6. Solaris10 修改hostname
  7. win10上跑 sqlserver 2000应用程序
  8. Ubuntu14.04下安装Libsvm,并使用Libsvm
  9. win7下命令行提示符从C盘进入E盘的文件夹下
  10. Linux服务器 java生成的图片验证码乱码问题