跳轉到內容

程式語言導論/閉包

來自華夏公益教科書,開放的書籍,開放的世界
閉包可以實現為一對指標 (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;
}

定義和示例 · 部分應用

華夏公益教科書