我问了一个关于curry和闭包的问题。 什么是闭包?它和咖喱有什么关系?


当前回答

下面的示例演示了Scheme编程语言中的闭包。

首先定义一个函数,该函数定义了一个局部变量,在函数外部不可见。

; Function using a local variable
(define (function)
  (define a 1)
  (display a) ; prints 1, when calling (function)
  )
(function) ; prints 1
(display a) ; fails: a undefined

下面是同样的例子,但是现在函数使用了一个全局变量,定义在函数外部。

; Function using a global variable
(define b 2)
(define (function)
  (display b) ; prints 2, when calling (function)
  )
(function) ; prints 2
(display 2) ; prints 2

最后,这里有一个函数携带自己闭包的例子:

; Function with closure
(define (outer)
  (define c 3)
  (define (inner)
    (display c))
  inner ; outer function returns the inner function as result
  )
(define function (outer))
(function) ; prints 3

其他回答

为了帮助理解闭包,研究一下如何在过程语言中实现闭包可能会很有用。本解释将遵循Scheme中闭包的简单实现。

首先,我必须介绍名称空间的概念。在Scheme解释器中输入命令时,它必须计算表达式中的各种符号并获得它们的值。例子:

(define x 3)

(define y 4)

(+ x y) returns 7

define表达式将值3存储在x的位置,将值4存储在y的位置。然后当我们调用(+ x y)时,解释器在命名空间中查找值,并能够执行该操作并返回7。

但是,在Scheme中,有些表达式允许您临时覆盖符号的值。这里有一个例子:

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

let关键字所做的是引入一个新的名称空间,x的值为5。你会注意到,它仍然可以看到y = 4,使得返回的和为9。您还可以看到,一旦表达式结束,x又回到了3。在这种情况下,x被局部值暂时掩盖了。

过程式语言和面向对象语言都有类似的概念。无论何时在函数中声明一个与全局变量同名的变量,都会得到相同的效果。

我们如何实现它呢?一种简单的方法是使用链表——头部包含新值,尾部包含旧的名称空间。当你需要查找一个符号时,你从头部开始,一直到尾部。

现在让我们暂时跳过第一类函数的实现。函数或多或少是在函数被调用时执行的一组指令,最终返回值。当我们读入一个函数时,我们可以在后台存储这些指令,并在函数被调用时运行它们。

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

我们定义x为3,加-x为它的参数y,加上x的值。最后,在x被一个新的x遮蔽的环境中,我们调用加-x,这个x的值为5。如果我们只存储函数+ x的(+ xy)操作,因为我们在x = 5的情况下,返回的结果将是9。这就是所谓的动态作用域。

然而,Scheme、Common Lisp和许多其他语言都有所谓的词法作用域——除了存储操作(+ x y)之外,我们还将名称空间存储在特定的位置。这样,当我们查找值的时候,我们可以看到x,在这里,实际上是3。这是一个闭包。

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

总之,我们可以使用链表来存储函数定义时名称空间的状态,从而允许我们从封闭的作用域访问变量,并且能够在不影响程序其余部分的情况下对变量进行局部屏蔽。

如果您来自Java世界,您可以将闭包与类的成员函数进行比较。看看这个例子

var f=function(){
  var a=7;
  var g=function(){
    return a;
  }
  return g;
}

函数g是一个闭包:g封闭a。g可以和成员函数比较,a可以和类域比较,函数f可以和类比较。

在Groovy中有一个简单的例子供您参考:

def outer() {
    def x = 1
    return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1

闭包是JavaScript中的一个特性,函数可以访问自己的作用域变量、外部函数变量和全局变量。

闭包即使在外部函数返回之后也可以访问它的外部函数作用域。这意味着闭包可以记住并访问它的外部函数的变量和参数,即使函数已经完成。

内部函数可以访问在自己的作用域、外部函数的作用域和全局作用域中定义的变量。外部函数可以访问在自己的作用域和全局作用域中定义的变量。

闭包的例子:

var globalValue = 5;

function functOuter() {
  var outerFunctionValue = 10;

  //Inner function has access to the outer function value
  //and the global variables
  function functInner() {
    var innerFunctionValue = 5;
    alert(globalValue + outerFunctionValue + innerFunctionValue);
  }
  functInner();
}
functOuter();  

输出将为20,即其内部函数自身变量、外部函数变量和全局变量值的和。

封闭非常简单。我们可以这样考虑: 闭包=函数+它的词汇环境

考虑以下函数:

function init() {
    var name = “Mozilla”;
}

在上述情况下,闭包是什么? 函数init()及其词法环境中的变量,即name。 闭包= init() + name

考虑另一个函数:

function init() {
    var name = “Mozilla”;
    function displayName(){
        alert(name);
}
displayName();
}

这里的闭包是什么? 内部函数可以访问外部函数的变量。displayName()可以访问父函数init()中声明的变量名。但是,如果displayName()中存在相同的局部变量,则将使用它们。

闭包1:init函数+ (name变量+ displayName()函数)——>词法作用域

闭包2:displayName函数+(名称变量)——>词法范围