闭包分析:lua function,oc 代码块可以产生闭包.c++lambda不能产生闭包
2016-03-16 17:25
375 查看
什么是闭包?这个问题说简单挺简单的,但是一般讲了之后跟没讲一样。就拿下面的lua代码看一下。
上面的fun获得的就是一个闭包,虽然获取的只是一个函数,但是它包含了getCallBack里的变量x,x我们称之为upvalue。很奇怪吧,getCallBack调用完,理论上它分配的栈会被回收,x也会被清除了,可是fun这个函数变量将会一直把持x。为什么fun一直能把持x呢?getCallBack在return之前,它应该把x压入了调用它的函数的栈中了,return时再把函数地址压入栈中,x与这个函数地址值构成了一个闭包形成一个整体。而调用这个闭包中函数时,先在此函数作用域找x,由于找不到,然后到它的闭包环境中去找。上面fun的输出是2、3、4,因为x一直保存在闭包环境中,每次调用x加一
下面是闭包英文解释,讲的就是闭包是使用了自由的变量,使用自由变量这话完全听不懂,太玄乎,不自由变量是什么?大家只知道静态变量,常变量,局部变量什么的,这个还真没听过。不过后面一句话说的很清楚,闭包是在闭包成员环境中创建的,闭包是一组环境变量加一个函数,此函数可以访问这些变量。这些变量lua里称为upvalue
Closures are functions that refer to independent
(free) variables. In other words, the function defined in the closure'remembers'
the environment in which it was created.
oc里的代码块也产生闭包,下面定义了一个代码块block。传到闭包环境中的变量默认是常量,只能访问,不能修改的,要想修改必须在变量声明前加上__block。
c++中的lambda表达式产生不了闭包,看起来能。。。,下面是lambda的定义
[capture list] (parameter list) ->return type { function body }
下面是一个例子,[]中的是捕获列表,lua里传入闭包环境中的变量可读写,都是直接对变量本身操作,没有中间变量作为参数。oc中默认传入的是常量,只读,通过__block关键字可以设置为可写。而c++默认传入闭包环境的也是常量,你要想修改这些变量,需要在想修改的变量前加&,如果像下面一样就是所有传入闭包的都可以修改。上面lambda的定义很清晰了,就不解释了。
下面代码的输出是32768、32768、32768,显然x没有被加入到闭包环境中,也就说明闭包根本不存在
总的说来,闭包有利也有弊,并不鼓励过多使用,用的太多了逻辑会变混乱,后期维护也会变麻烦。闭包的后面其实是函数对象这个东西,在lua里,js里函数对象也是基本类型,用起来得心应手,而编译型的高级语言一般很少提供的,不过现在许多都提供了类似类型,oc使用了代码块,java、c++都加入lamdba大军了。不知道函数式编程是种什么体验,有时间好好专研下,不知道跟函数对象有什么关联。
local function getCallBack() local x = 1 return function() x = x + 1 print(x) end end local fun = getCallBack() fun() fun() fun()
上面的fun获得的就是一个闭包,虽然获取的只是一个函数,但是它包含了getCallBack里的变量x,x我们称之为upvalue。很奇怪吧,getCallBack调用完,理论上它分配的栈会被回收,x也会被清除了,可是fun这个函数变量将会一直把持x。为什么fun一直能把持x呢?getCallBack在return之前,它应该把x压入了调用它的函数的栈中了,return时再把函数地址压入栈中,x与这个函数地址值构成了一个闭包形成一个整体。而调用这个闭包中函数时,先在此函数作用域找x,由于找不到,然后到它的闭包环境中去找。上面fun的输出是2、3、4,因为x一直保存在闭包环境中,每次调用x加一
下面是闭包英文解释,讲的就是闭包是使用了自由的变量,使用自由变量这话完全听不懂,太玄乎,不自由变量是什么?大家只知道静态变量,常变量,局部变量什么的,这个还真没听过。不过后面一句话说的很清楚,闭包是在闭包成员环境中创建的,闭包是一组环境变量加一个函数,此函数可以访问这些变量。这些变量lua里称为upvalue
Closures are functions that refer to independent
(free) variables. In other words, the function defined in the closure'remembers'
the environment in which it was created.
oc里的代码块也产生闭包,下面定义了一个代码块block。传到闭包环境中的变量默认是常量,只能访问,不能修改的,要想修改必须在变量声明前加上__block。
int a = 3; int b = 5; void (^block)() = ^(){printf("%d %d\n", a, b);}; block();上面代码还不能说明产生了闭包,下面代码跟开头的lua代码功效一样输出是2、3、4,可以看出是个闭包。先声明block类型,然后创建getBlock函数,它返回的是一个闭包,x在这个闭包环境中。__block指示了x可以被修改,闭包中函数每次调用加一并且输出x的值。
typedef void(^block)(); block getBlock(){ __block int x = 1; return ^(){ x += 1; printf("%d\n", x); }; } int main(int argc, const char * argv[]) { block b = getBlock(); b(); b(); b(); }
c++中的lambda表达式产生不了闭包,看起来能。。。,下面是lambda的定义
[capture list] (parameter list) ->return type { function body }
下面是一个例子,[]中的是捕获列表,lua里传入闭包环境中的变量可读写,都是直接对变量本身操作,没有中间变量作为参数。oc中默认传入的是常量,只读,通过__block关键字可以设置为可写。而c++默认传入闭包环境的也是常量,你要想修改这些变量,需要在想修改的变量前加&,如果像下面一样就是所有传入闭包的都可以修改。上面lambda的定义很清晰了,就不解释了。
auto lam = [&]()->int { return 50; };
下面代码的输出是32768、32768、32768,显然x没有被加入到闭包环境中,也就说明闭包根本不存在
std::function<void(void)> get(){ int x = 0; return [&]()->void{ x += 1; printf("%d\n", x); }; } int main(int argc, const char * argv[]) { auto fun = get(); fun(); fun(); fun(); }
总的说来,闭包有利也有弊,并不鼓励过多使用,用的太多了逻辑会变混乱,后期维护也会变麻烦。闭包的后面其实是函数对象这个东西,在lua里,js里函数对象也是基本类型,用起来得心应手,而编译型的高级语言一般很少提供的,不过现在许多都提供了类似类型,oc使用了代码块,java、c++都加入lamdba大军了。不知道函数式编程是种什么体验,有时间好好专研下,不知道跟函数对象有什么关联。
相关文章推荐
- 纯lua实现 utf-16le 和 utf-8互转
- 报错 function refid '%d' does not reference a Lua function
- Lua中的函数(function)、可变参数、局部函数、尾递归优化等实例讲解
- Lua语言在Wireshark中使用(转)
- lua和整合实践
- UTF8字符串在lua的截取和字数统计【转载】
- <转> Lua使用心得(2)
- (转) Lua使用心得一 LUA和VC整合
- Failed to notify ProjectEvaluationListener.afterEvaluate(), but primary configuration failure takes
- lua相关
- lua 远程调试 【zeroBrane 使用mobdebug】(good转)
- ZeroBrane Studio远程调试Lua程序(转)
- Lua中的metatable详解
- 使用lua实现几个小算法
- ngx_lua实现重启php
- 156.Evaluate the following SQL statement:
- uLua学习笔
- Lua中的string库(字符串函数库)总结
- LUA string库详解
- Lua -- select用法