跳转到内容

编程语言导论/闭包

来自维基教科书,开放的书籍,开放的世界
闭包可以实现为一对指针 (f, t)。第一个元素 f 指向函数的实现。第二个元素 t 指向一个表,该表包含函数体中使用的自由变量。

一个闭包 是一个函数的实现,以及一个将值绑定到函数体中出现的自由变量的表。一个变量 *v* 在函数体 *f* 中是自由的,如果 *v* 在 *f* 内部使用,但没有在 *f* 中声明。闭包为开发者提供了一种将函数传递出去的方式,以及关于这些函数创建上下文的某些信息。从实际的角度来看,闭包允许开发者编写*函数工厂*。例如,下面我们有一个 Python 函数,它产生一元求和

def unarySumFactory(a):
  def unarySum(b): return a + b
  return unarySum  
                 
inc = unarySumFactory(1)
  
print inc(2)
  
sum2 = unarySumFactory(2)
  
print sum2(2)

在上面的例子中,我们注意到变量 *a* 在函数 unarySum 的主体中是自由的。这个变量在函数 unarySumFactory 的作用域中声明,并且它可以在 unarySum 内部引用这一事实为语言设计者提出了一个实现问题。通常,一旦函数返回它的值,为它的激活记录保留的空间就会被释放。但是,如果这个空间被释放,我们例子中的变量 *a* 一旦 unarySumFactory 返回一个值,就将不再有存储位置。为了绕过这个困难,闭包被实现为一对 *(f,t)*,其中 *f* 是指向函数实现的指针,*t* 是指向一个表,该表包含与值关联的所有在 *f* 中使用的自由变量。

因为闭包可能比创建它的函数存活更久,所以通常 *(f,t)* 这对,以及表 *t* 的内容会在中分配。下面的代码是用 C 写的,实现了之前看到的 unarySumFactory 调用。C 没有语法支持闭包。但是,我们可以通过将高阶函数调用与动态堆分配结合起来来实现闭包。

#include <stdio.h>
#include <stdlib.h>
 
typedef struct struct_free_variables_unarySum {
  int x;
} FREE_VARIABLES_unarySum;
 
typedef struct {
  FREE_VARIABLES_unarySum* t;
  int (*f)(FREE_VARIABLES_unarySum* t, int x);
} CLOSURE_unarySum;
 
int unarySum(FREE_VARIABLES_unarySum* t, int y) {
  return y + t->x;
};
 
void* unarySumFactory(int x) {
  CLOSURE_unarySum* c = (CLOSURE_unarySum*) malloc (sizeof(CLOSURE_unarySum));
  c->t = (FREE_VARIABLES_unarySum*) malloc (sizeof(FREE_VARIABLES_unarySum));
  c->t->x = x;
  c->f = &unarySum;
  return c;
}
 
int test(int n) {
  CLOSURE_unarySum* c = unarySumFactory(2);
  int retVal = c->f(c->t, n);
  free(c->t);
  free(c);
  return retVal;
}
 
int main(int argc, char** argv) {
  printf("%d\n", test(argc));
  return 0;
}

定义和示例 · 部分应用

华夏公益教科书