闭包函数是什么

闭包函数是什么:

首先看如下代码,你是否了解其真正的意义:

function test()
        local i=0
        return function()
            i=i+1
            return i
        end
end

doTest=test()
print(doTest())   --输出1
print(doTest())   --输出2

 

你可能有这样的疑问:
1. 调用test()返回的函数doTest()时,变量 i 的定义在哪?
2. test()中的i不是局部变量吗?为什么返回的函数还能调用。

如果你有这样的疑问,请接着往下看。
首先,了解几个概念:

词法定界:当一个函数内嵌套另一个函数的时候,内函数可以访问外部函数的局部变量,这种特征叫做词法定界。而这些变量就被称为该内嵌函数的upvalue,upvalue实际指的是变量而不是值,这些变量可以在内部函数之间共享

闭包:通过调用含有一个内部函数加上该外部函数持有的外部局部变量(upvalue)的外部函数(外部函数就是工厂)产生的一个实例函数。

闭包组成:外部函数+外部函数创建的upvalue+内部函数(闭包函数)

如上面的函数test(),
test()就是外部函数
外部函数的局部变量local i=0就是upvalue(也叫做非局部变量,之所以叫做非局部变量,是因为此变量的作用域既不是局部变量的作用域,也不是全局变量的作用域。),
返回的函数就是内部函数

现在再回头看看原来的函数,我们知道了这种形式的函数叫做闭包函数。而test()中的局部变量i是内嵌函数的upvalue(非局部变量),且在内部函数中共享。

重复调用内部函数时,每一个调用都会记住上一次调用后的值,就是说第一次调用doTest()之后,i 的值已经是1了。

下面再看个例子

function test()
        local i=0
        return function()
            i=i+1
            return i
        end
end

doTest=test()
doAgain=test()
print(doTest())   --输出1
print(doTest())   --输出2

print(doAgain())  --输出1
print(doAgain())  --输出2

 

可以看到,此时执行doAgain()时 i 值并没有在原来的基础上增加。

原因是:
doTest,doAgain是建立在同一个函数,同一个局部变量的不同实例上面的两个不同的闭包
调用一次test()就会产生一个新的闭包, 而闭包中的upvalue各自独立。所以不难解释为什么doAgain()的i 值为什么没有在原来的基础上增加了。

闭包函数有什么用:

在for in 的循环中需要使用到迭代器,而迭代器需要保留上一次调用的状态和下一次成功调用的状态,刚好可以使用闭包的机制来实现。

创建迭代器:

function list_iter(t)     --外包函数叫做工厂函数。
            local i=0
            local n=table.getn(t)
            return function()
                i=i+1
                if i<=n then return t[i] end
            end
    end
--[[这里的list_iter是一个工厂,每次调用都会产生一个新的闭包。该闭包内部包括了upvalue(t,i,n)。
因此每调用一次该函数产生的闭包,那么该闭包就会根据记录上一次的状态,以及返回list的下一个。]]

 在while中使用:

--while中使用:
t={10,20,90}
iter=list_iter(t)  --调用迭代器产生一个闭包
while true do
--当闭包函数的i值已经等于n的值时,依然会执行闭包函数,此时返回的就是nil.
--如果没有下面的判断,while就会一直循环,并进入死循环。
    local element=iter()
    if element==nil then break end
    print(element)
end

 在泛型for使用

--泛型for使用:
t={10,0,29}
--这里的list_iter()工厂函数只会被调用一次产生一个闭包函数,
--后面的每一次迭代都是用该闭包函数,而不是工厂函数。
for element in list_iter(t) do
    print(element)
end

如果想要同时返回k,v值,需要修改工厂函数,如下所示:

function list_iter(tb)
     local i = 0
     return function ()
          i = i + 1
           --如果没有下面这个判断,就会一直执行。
           --详情可以看我的另一篇博客,《Lua内容关于for循环的总结》
          if tb[i] == nil then  
            return nil
          end   
          return i,tb[i]
     end
end

 

posted @ 2017-08-25 13:29  戈丫汝  阅读(370)  评论(3编辑  收藏  举报