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

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

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

Lua中的closure(闭合函数)的更多相关文章

  1. 简述C/C++调用lua中实现的自定义函数

    1.首先说下目的,为什么要这么做 ? 正式项目中,希望主程序尽量不做修改,于是使用C/C++完成功能的主干(即不需要经常变动的部分)用lua这类轻量级的解释性语言实现一些存在不确定性的功能逻辑:所以, ...

  2. 不要在Lua中使用os.clock()函数

    1.os.clock函数的实现是调用了c语言的函数函数库,实现代码如下: static int os_clock (lua_State *L) { lua_pushnumber(L, ((lua_Nu ...

  3. cocos2d-x 2.2.0 如何在lua中注册回调函数给C++

    cocos2d-x内部使用tolua进行lua绑定,但是引擎并没有提供一个通用的接口让我们可以把一个lua函数注册给C++层面的回调事件.翻看引擎的lua绑定代码,我们可以仿照引擎中的方法来做.值得吐 ...

  4. lua中易混淆函数

    lua中易混淆的函数 ipairs和pairs: ipairs只能顺序遍历table,遇到key不是数字就会退出 pairs可以遍历table中所有元素 ----------------------- ...

  5. lua中调用C++函数

    lua中调用C++函数 我们产品中提供了很多lua-C API给用户在lua中调用,之前一直没用深究其实现原理,只是根据已有的代码在编码.显然这不是一个好的习惯,没用达到知其所以然的目的. 一.基本原 ...

  6. Lua中的require

    lua中的require机制    为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来.现在看看lua的require的处理流程.1.require机制相关 ...

  7. 【转载】lua中的require机制

    [转载自]http://blog.chinaunix.net/uid-552961-id-2736410.html lua中的require机制 为了方便代码管理,通常会把lua代码分成不同的模块,然 ...

  8. Lua中的require(转)

    lua中的require机制    为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来.现在看看lua的require的处理流程.1.require机制相关 ...

  9. lua中的require机制

    lua中的require机制 为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来.现在看看lua的require的处理流程.1.require机制相关的数据 ...

随机推荐

  1. js dom 创建table标签和子属性, 以及创建多选框

    代码: <div class="Category"> <span id="Edit_headerTitle">Edit Categori ...

  2. e812. 强制弹出菜单为重组件

    By default, Swing popup menus used by JMenu and JPopupMenu are lightweight. If heavyweight component ...

  3. Unity GUI(uGUI)使用心得与性能总结

    Unity GUI(uGUI)使用心得与性能总结 作者 kingshijie 关注 2015.09.26 15:35 字数 3686 阅读 28031评论 10喜欢 49 背景和目的 小哈接触Unit ...

  4. 核心动画——Core Animation

    一. CALayer (一). CALayer简单介绍 在iOS中,你能看得见摸得着的东西基本上都是UIView,比方一个button.一个文本标签.一个文本输入框.一个图标等等.这些都是UIView ...

  5. css 阻止元素中的文本。双击选中

    //firefox -moz-user-select: none; //chrome.safari -webkit-user-select: none; //ie -ms-user-select: n ...

  6. 多个IoC容器适配器设计及性能测试(Castle.Windsor Autofac Spring.Core)

    [转]多个IoC容器适配器设计及性能测试和容器选择 1. 采用的IoC容器和版本 Autofac.2.6.3.862 Castle.Windsor.3.1.0 Spring.Core.2.0.0 2. ...

  7. tomcat启动时设定环境变量

    在tomcat的bin目录中修改startup.bat 设置CATALINA_HOME set "CATALINA_HOME=F:\solr\apache-tomcat\apache-tom ...

  8. Android学习笔记——Intents 和 Intent Filters(二)

    本人邮箱:JohnTsai.Work@gmail.com,欢迎交流讨论. 欢迎转载,转载请注明网址:http://www.cnblogs.com/JohnTsai/p/3993488.html 知识点 ...

  9. Android 代码自动提示功能

    Eclipse for android 实现代码自动提示智能提示功能,介绍 Eclipse for android 编辑器中实现两种主要文件 java 与 xml 代码自动提示功能,解决 eclips ...

  10. Unable to resolve target 'android-9'

    右键项目文件--->properties--->android  选择对应版本 保存 如还不生效 打开项目文件project.properties ,修改 target=android-1 ...