lua的函数为数据类型之一, 也叫first-class.

所以可以把函数赋予给变量, 这个我在前一篇BLOG中写过, 例如



function f(x) return x*2 end



和f = function(x) return x*2 end



是一样的写法.


甚至可以把函数作为返回值, 例如 :

function f(x) 
  return function (x) return x*2 end 
end



这里其实返回了一个匿名函数,



a = f(1)
> a = f(1)
> = a
function: 0x12a0ab0
> =a(1)
2
> return a(2)
4



那么什么是closure呢?



我们来看一个函数 :

function newc()
  local i = 0  
  return function ()
    i = i+1  -- 非本地, 非全局, 函数中的函数可以访问它上层的本地变量, 并可以逃逸这个变量, 成为一个非本地, 非全局变量.
    return i
  end
end



这个函数返回一个函数, 在返回的匿名函数中, i其实是newc函数的本地变量, 对匿名函数来说, i变量非本地, 非全局.



那么把newc()的结果匿名函数赋予给一个变量后, i就变成了这个函数的一部分, 



closure包含"函数的定义"以及"函数需要访问的非本地, 非全局变量, 这个例子是i".



我们可以理解为closure实际上包含了两部分信息, 一部分是定义区域, 一部分是函数自有的变量区域(非本地, 非全局.)



利用这种方式创建的函数, 自由变量可以用作计数器, 例如 :

> c1 = newc()
> = c1
function: 0x1291a70
> return  c1()  -- i这个自由变量在当前这个c1函数/closure内自增.
1
> return  c1()
2
> return  c1()
3
> c2 = newc()
> = c2   -- i这个自由变量在当前这个c2函数/closure内自增. 和c1/closure没有关系.
function: 0x12a2290
> return  c2()
1
> return  c2()
2
> return  c1()
4




由于函数名只是一个closure的名称, 所以可以很方便的复写. 例如



> do 
  local oldsin = math.sin  -- 把math.sin这个函数名指向的closure赋予给本地变量oldsin. 对外就隐藏了
  local k = math.pi/180  
  math.sin = function(x)    -- 复写math.sin
    return oldsin(x*k)
  end
end



那么以后调用math.sin就是新的closure了, 老的closure因为被指向为一个本地变量, 已经无法被使用了.



> = oldsin(1)
stdin:1: attempt to call global 'oldsin' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: in ?
> = math.sin(1)
0.00030461741507571



这种用法类似黑盒, 把一个closure隐藏, 对外使用新的一个closure.



还有一个例子是对文件操作加一个权限判断, 隐藏没有权限判断的io.open函数.

> do 
  local oldopen = io.open  -- 把老的io.open存储到一个本地变量, 对外隐藏.
  local access_ok = function (filename, mode)  -- 权限判断函数
     -- check access, return boolean
    end
  io.open = function (filename, mode)  -- 复写io.open
    if access_ok(filename, mode) then
      return oldopen(filename,mode)  -- 在黑盒中访问外部的本地变量oldopen, 这个新的io.open函数/closure包含了oldopen这个变量.
    else
      return nil, "access denied"
    end
  end
end

-- 那么以后调用io.open时其实调用的是复写的io.open.

小结.


1. Lua函数或closure的一个重大功能是, 对上层函数的本地变量的可访问, 以及逃逸(逃逸这个说得明白点就是closure中包含变量信息, 同时包含定义信息).


2. 支持复写, 同时结合本地变量的逃逸, 很好的达到黑盒目的.