转自:http://book.luaer.cn/_41.htm

当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。虽然这看起来很清楚,事实并非如此,词法定界加上第一类函数在编程语言里是一个功能强大的概念,很少语言提供这种支持。

下面看一个简单的例子,假定有一个学生姓名的列表和一个学生名和成绩对应的表;现在想根据学生的成绩从高到低对学生进行排序,可以这样做:

  

names = {"Peter", "Paul", "Mary"}
grades = {Mary = 10, Paul = 7, Peter = 8}
table.sort(names, function (n1, n2)
return grades[n1] > grades[n2] -- compare the grades
end)

  

假定创建一个函数实现此功能:

function sortbygrade (names, grades)
table.sort(names, function (n1, n2)
return grades[n1] > grades[n2] -- compare the grades
end)
end

  

例子中包含在sortbygrade函数内部的sort中的匿名函数可以访问sortbygrade的参数grades,在匿名函数内部 grades 不是全局变量也不是局部变量,我们称作外部的局部变量(external local variable)或者upvalue。(upvalue意思有些误导,然而在Lua中他的存在有历史的根源,还有他比起external local variable简短)。

看下面的代码:

function newCounter()
local i = 0
return function() -- anonymous function
i = i + 1
return i
end
end print(newCounter()) --> function: 08935750 ?
print(newCounter()) --> function: 05B09710 ? c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2

  

匿名函数使用upvalue i保存他的计数,当我们调用匿名函数的时候 i 已经超出了作用范围,因为创建i的函数newCounter已经返回了。然而Lua用闭包的思想正确处理了这种情况。简单的说,闭包是一个函数以及它的upvalues。如果我们再次调用newCounter,将创建一个新的局部变量i,因此我们得到了一个作用在新的变量i上的新闭包。

c2 = newCounter()
print(c2()) --> 1 这里要特别注意!
print(c1()) --> 3 这里要特别注意!
print(c2()) --> 2

  

c1、c2是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包

技术上来讲,闭包指值而不是指函数,函数仅仅是闭包的一个原型声明;尽管如此,在不会导致混淆的情况下我们继续使用术语函数代指闭包。

闭包在上下文环境中提供很有用的功能,如前面我们见到的可以作为高级函数(sort)的参数;作为函数嵌套的函数(newCounter)。这一机制使得我们可以在Lua的函数世界里组合出奇幻的编程技术。闭包也可用在回调函数中,比如在GUI环境中你需要创建一系列button,但用户按下button时回调函数被调用,可能不同的按钮被按下时需要处理的任务有点区别。具体来讲,一个十进制计算器需要10个相似的按钮,每个按钮对应一个数字,可以使用下面的函数创建他们:

function digitButton (digit)
return Button{ label = digit,
action = function ()
add_to_display(digit)
end
}
end

  

这个例子中我们假定Button是一个用来创建新按钮的工具, label是按钮的标签,action是按钮被按下时调用的回调函数。(实际上是一个闭包,因为他访问upvalue digit)。digitButton完成任务返回后,局部变量digit超出范围,回调函数仍然可以被调用并且可以访问局部变量digit。

闭包在完全不同的上下文中也是很有用途的。因为函数被存储在普通的变量内我们可以很方便的重定义或者预定义函数。通常当你需要原始函数有一个新的实现时可以重定义函数。例如你可以重定义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)
end
end

  

这样我们把原始版本放在一个局部变量内,访问sin的唯一方式是通过新版本的函数。

利用同样的特征我们可以创建一个安全的环境(也称作沙箱,和java里的沙箱一样),当我们运行一段不信任的代码(比如我们运行网络服务器上获取的代码)时安全的环境是需要的,比如我们可以使用闭包重定义io库的open函数来限制程序打开的文件。

do
local oldOpen = io.open
io.open = function (filename, mode)
if access_OK(filename, mode) then
return oldOpen(filename, mode)
else
return nil, "access denied"
end
end
end

  

lua 11 闭包,函数的使用的更多相关文章

  1. day11_7.11 闭包函数与装饰器

    补充: callable 代表可调用的,加括号可以执行.(函数或者类) import this  查看python之禅 一.闭包函数 所谓闭包函数,就是定义在函数内部的函数,也就是函数定义的嵌套.而在 ...

  2. 三种语言(c++、as、lua)中函数的差异性

    对于不同的语言, 尤其是静态语言和动态语言, 对于函数的定义(即如何看待一个函数)和处理截然不同.具体来说可以分为两类: 1.将函数视为第一类型值, 即函数和其他的对象一样, 都是语言中一个普通的对象 ...

  3. lua 函数调用 -- 闭包详解和C调用

    转自:http://www.cnblogs.com/ringofthec/archive/2010/11/05/luaClosure.html 这里, 简单的记录一下lua中闭包的知识和C闭包调用 前 ...

  4. lua二进制操作函数

    由于 Lua 脚本语言本身不支持对数字的二进制操作(例如 与,或,非 等操作),MUSHclient 为此提供了一套专门用于二进制操作的函数,它们都定义在一个“bit”表中,使用时只要requre “ ...

  5. Lua中的函数

    [前言] Lua中的函数和C++中的函数的含义是一致的,Lua中的函数格式如下: function MyFunc(param) -- Do something end 在调用函数时,也需要将对应的参数 ...

  6. 深入理解Lua的闭包一:概念、应用和实现原理

    本文首先通过具体的例子讲解了Lua中闭包的概念,然后总结了闭包的应用场合,最后探讨了Lua中闭包的实现原理.   闭包的概念 在Lua中,闭包(closure)是由一个函数和该函数会访问到的非局部变量 ...

  7. [转]lua数据结构--闭包

    前面几篇文章已经说明了Lua里面很常用的几个数据结构,这次要分享的也是常用的数据结构之一 – 函数的结构.函数在Lua里也是一种变量,但是它却很特殊,能存储执行语句和被执行,本章主要描述Lua是怎么实 ...

  8. Lua的闭包详解(终于搞懂了)

    词法定界:当一个函数内嵌套另一个函数的时候,内函数可以访问外部函数的局部变量,这种特征叫做词法定界 table.sort(names,functin (n1,n2) return grades[n1] ...

  9. php的匿名函数和闭包函数

    php的匿名函数和闭包函数 tags: 匿名函数 闭包函数 php闭包函数 php匿名函数 function use 引言:匿名函数和闭包函数都不是特别高深的知识,但是很多刚入门的朋友却总是很困惑,因 ...

随机推荐

  1. eNSP仿真软件之利用三层交换机实现VLAN间路由

    1.实验原理: VLAN将一个物理的LAN在逻辑上划分成多个广播域.VLAN内的主机间可以直接通信,而VLAN间不能直接互通. 在现实网络中,经常会遇到需要跨VLAN相互访问的情况,工程师通常会选择一 ...

  2. Day9 - Python基础9 socket基础、粘包

    本节内容: 1.socket的介绍 2.基于tcp的socket 3.基于tcp的问题分析 4.基于udp的socket 5.基于udp的问题分析 6.基于udp的ntp服务 7.基于tcp的远程执行 ...

  3. 0. gitlab 一些常用知识

    Monitor 但是有反映  提交慢的情况时候.  可以查看一下队列 使用root账号 gitlab最多可以同时25个队列.  多了需要排队. 可以查看一下原因.

  4. 2019面向对象程序设计(Java) 第16周学习指导及要求

    2019面向对象程序设计(Java)第16周学习指导及要求 (2019.12.13-2019.12.16) 学习目标 (1) 掌握Java应用程序的打包操作: (2) 掌握线程概念: (3) 掌握线程 ...

  5. python-11-字典的增删改查

    前言 1.dict 字典:{key,vlaue} --key 必须是不可变数据类型,可哈希,--value:任意数据类型 2.dict优点:二分查找去查询--存储大量的关系型数据,可哈希.--无序的, ...

  6. Ajax和跨域请求

    Ajax 一.概述 Web 程序最初的目的就是将信息(数据)放到公共的服务器,让所有网络用户都可以通过浏览器访问. 在次之前,我们可以通过以下几种方式让浏览器发出对服务端的请求,获取服务端的数据: 地 ...

  7. Tkinter最佳实践(半小时)

    概述: 简介 Tkinter模块("Tk 接口")是Python的标准Tk GUI工具包的接口.Tk和Tkinter可以在大多数的Unix平台下使用,同样可以应用在Windows和 ...

  8. 使用requests模块进行封装,帮你如何处理restful类型的接口

    import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) c ...

  9. 安全性测试:OWASP ZAP 2.8 使用指南(一):安全测试基础及ZAP下载、安装

    概览 本文意在对于OWASP's Zed Attack Proxy(ZAP)软件做一个基本使用指南介绍. ZAP是一个用于实施安全性测试的工具,即使没有很强的安全测试背景也可以很好的使用. 为了达到这 ...

  10. JS 查找数组的父节点及祖先节点

    function findAllParent(node, tree, parentNodes=[], index = 0){ if(!node || node.parentId === 0){ ret ...