什么是闭包

超、凢脫俗 2022-08-23 12:47 255阅读 0赞
  1. \----本文摘自programming in lua

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)

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

  1. 下面看一个简单的例子,假定有一个学生姓名的列表和一个学生名和成绩对应的表,现在想根据学生的成绩从高到低对学生进行排序,可以这样做:
  2. names = {"Peter", "Paul", "Mary"}
  3. grades = {Mary = 10, Paul = 7, Peter = 8}
  4. table.sort(names, function (n1, n2)
  5. return grades[n1] > grades[n2] -- compare the grades
  6. end)

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

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

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

看下面的代码:

  1. function newCounter()
  2. local i = 0
  3. return function() -- anonymous function
  4. i = i + 1
  5. return i
  6. end
  7. end
  8. c1 = newCounter()
  9. print(c1()) --> 1
  10. print(c1()) --> 2

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

  1. c2 = newCounter()
  2. print(c2()) --> 1
  3. print(c1()) --> 3
  4. print(c2()) --> 2

c1、c2 是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包。 技术上来讲,闭包指值而不是指函数,函数仅仅是闭包的一个原型声明;尽管如此,在不会导致混淆的情况下我们继续使用术语函数代指闭包。 闭包在上下文环境中提供很有用的功能,如前面我们见到的可以作为高级函数(sort) 的参数;作为函数嵌套的函数(newCounter)。这一机制使得我们可以在 Lua 的函数世界 里组合出奇幻的编程技术。闭包也可用在回调函数中,比如在 GUI 环境中你需要创建一 系列 button,但用户按下 button 时回调函数被调用,可能不同的按钮被按下时需要处理 的任务有点区别。具体来讲,一个十进制计算器需要 10 个相似的按钮,每个按钮对应一 个数字,可以使用下面的函数创建他们:

  1. function digitButton (digit)
  2. return Button{ label = digit,
  3. action = function ()
  4. add_to_display(digit)
  5. end
  6. }
  7. end

这个例子中我们假定 Button 是一个用来创建新按钮的工具, label 是按钮的标签,action 是按钮被按下时调用的回调函数。(实际上是一个闭包,因为他访问 upvalue digit)。digitButton 完成任务返回后,局部变量 digit 超出范围,回调函数仍然可以被调用并且可以访问局部变量 digit。 闭包在完全不同的上下文中也是很有用途的。因为函数被存储在普通的变量内我们可以很方便的重定义或者预定义函数。通常当你需要原始函数有一个新的实现时可以重定义函数。例如你可以重定义 sin 使其接受一个度数而不是弧度作为参数:

  1. oldSin = math.sin
  2. math.sin = function (x)
  3. return oldSin(x*math.pi/180)
  4. end

更清楚的方式:

  1. do
  2. local oldSin = math.sin
  3. local k = math.pi/180
  4. math.sin = function (x)
  5. return oldSin(x*k)
  6. end
  7. end

这样我们把原始版本放在一个局部变量内,访问 sin 的唯一方式是通过新版本的函数。 利用同样的特征我们可以创建一个安全的环境(也称作沙箱,和 java 里的沙箱一样),当我们运行一段不信任的代码(比如我们运行网络服务器上获取的代码)时安全的环境是需要的,比如我们可以使用闭包重定义 io 库的 open 函数来限制程序打开的文件。

  1. do
  2. local oldOpen = io.open
  3. io.open = function (filename, mode)
  4. if access_OK(filename, mode) then
  5. return oldOpen(filename, mode)
  6. else
  7. return nil, "access denied"
  8. end
  9. end
  10. end

发表评论

表情:
评论列表 (有 0 条评论,255人围观)

还没有评论,来说两句吧...

相关阅读

    相关 什么

    我们都知道一个概念。 在JS当中,一个函数可以访问其外部的变量资源。 ![format_png][] 一个典型的代码   但以下这种情况会出错。 fun

    相关 什么

    闭包 我们都知道,js的作用域分两种,全局和局部,基于我们所熟悉的作用域链相关知识,我们知道在js作用域环境中访问变量的权利是由内向外的,内部作用域可以获得当前作用域下的

    相关 什么

            \----本文摘自programming in  lua 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包

    相关 什么

    什么是闭包? 简单来说,闭包是指外部可以访问另一个函数作用域内的变量的函数,一般是定义在外层函数中的内层函数。 为什么需要闭包? 局部变量无法共享和长久的保存,而全局变量

    相关 什么

    闭包是指有权访问另一个函数作用域中变量的函数 作用:访问另一个函数作用域中变量 原理:通过匿名式函数把局部变量驻留在内存里,可以减少全局变量的使用 优点是封装性,

    相关 什么

    什么是闭包? 说明 闭包   有些读者可能很好奇,为什么闭包会成为一个独立的概念,以及为什么它需要编程语言提供额外的支持。这些疑惑可以本文中找到答案。

    相关 什么

    1.什么是闭包? 古老定义   闭包(closure),是指函数变量可以保存在函数作用域内,因此看起来是函数将变量“包裹”了起来   那这样说来,包含变量的函数就是闭

    相关 什么

    闭包 定义:闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链