Hello World
Spiga

高阶函数、委托与匿名方法

2009-04-18 12:25 by 老赵, 29760 visits

http://www.infoq.com/cn/articles/higher-order-function

这是几个月前就写的文章,只是一直忙于QCon的各项事宜,最近才发表出来。这篇是对.NET中委托,匿名方法和高阶函数使用的一个介绍,非常简单,目的是强调一下C#中对于高阶函数的使用,体现一下现在C#的高度生产力。我现在经常提起,Java是一个固步自封的语言,开发效率之低已经是难以辩驳的事实。对于Java粉丝来说,他们可以强调Java的自由文化,Java的开源之美,但是我相信没有人会继续强调Java语言的生产力如何——除非他不愿接受Ruby,Python,甚至一直被认作是“后辈”的C#也已经大大超越Java了。

语言是工具,带来的生产力很重要。最简单验证方法,便是尝试着去写一点Java程序,会发现很多时候寸步难行——如果你熟悉C# 3.0的话,这时候你就会怀念C#,Ruby,Python带来的生产力了。强调语言不代表忽视其他。语言是非常重要的,直接影响生产力,其重要性甚至超过“框架”和“类库”。因为框架类库可以由第三方补充,而语言不行。你可以说ruby某个功能1行就能做,而C#要5行,那么我可以补充一个类库,完成1行的功能。而Java就做不到。这也是为什么现在不少编程语言的书,都喜欢和Java做对比,因为Java的生产力的确是“难以提高”。如果它们拿C#出来进行比较——那么好,从CSDN的趣味题学C# 3.0,看出点什么意思了吗?

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

46 条回复

  1. 老农
    *.*.*.*
    链接

    老农 2009-04-18 12:32:00

    老赵也写软文了

  2. 老赵
    admin
    链接

    老赵 2009-04-18 12:32:00

    --引用--------------------------------------------------
    老农: 老赵也写软文了
    --------------------------------------------------------
    为什么说是软文?

  3. babyfaction1[未注册用户]
    *.*.*.*
    链接

    babyfaction1[未注册用户] 2009-04-18 12:52:00

    同感。java几乎任何功能都需要第3方库~~ 标准库几乎啥也做不了

  4. babyfaction1[未注册用户]
    *.*.*.*
    链接

    babyfaction1[未注册用户] 2009-04-18 12:54:00

    c#也是如此,简单的功能写一堆前奏~~

    大家来学ActionScript吧哇哈哈

  5. 张蒙蒙
    *.*.*.*
    链接

    张蒙蒙 2009-04-18 12:56:00

    我出身.net,现在用java,感受是java语法确实比不过C#了,感觉eclipse这个东西还有不少优点的,另外想说的是java的泛型确实很难用,还有集合,我还是习惯.net的集合,感觉java的集合访问代码啰嗦而且丑陋。
    不过开源确实不错,至少错了可以追寻到很深的地方。

  6. babyfaction1[未注册用户]
    *.*.*.*
    链接

    babyfaction1[未注册用户] 2009-04-18 12:59:00

    @张蒙蒙
    java和c#都啰嗦。。

  7. 老赵
    admin
    链接

    老赵 2009-04-18 13:07:00

    --引用--------------------------------------------------
    babyfaction1: 同感。java几乎任何功能都需要第3方库~~ 标准库几乎啥也做不了
    --------------------------------------------------------
    你和我说的不是一个东西,我是说语言,没有说类库。

  8. 老赵
    admin
    链接

    老赵 2009-04-18 13:08:00

    --引用--------------------------------------------------
    张蒙蒙: 我出身.net,现在用java,感受是java语法确实比不过C#了,感觉eclipse这个东西还有不少优点的,另外想说的是java的泛型确实很难用,还有集合,我还是习惯.net的集合,感觉java的集合访问代码啰嗦而且丑陋。
    不过开源确实不错,至少错了可以追寻到很深的地方。
    --------------------------------------------------------
    .NET也可以挖掘的,只是有多少人去挖掘呢?其实Java又有多少人挖掘,开源只是很多人的旗号而已。

  9. 老赵
    admin
    链接

    老赵 2009-04-18 13:08:00

    --引用--------------------------------------------------
    babyfaction1: c#也是如此,简单的功能写一堆前奏~~

    大家来学ActionScript吧哇哈哈
    --------------------------------------------------------
    这种罗嗦倒算了,一些“架子”语法罢了,C#的架子语法其实不多,最多也就是个Main方法吧。
    而且真要有C#脚本这种做法也已经有了,只是你没有去了解罢了。因此这些都不是语言能力的关键。我现在说的是语言功能强大与否,这是关键。

  10. Nick Wang
    *.*.*.*
    链接

    Nick Wang 2009-04-18 13:46:00

    开源的好处是,发现了bug可以自己马上改过来

  11. yeah[未注册用户]
    *.*.*.*
    链接

    yeah[未注册用户] 2009-04-18 14:07:00

    肯学自然会去挖掘的啦!

  12. T2噬菌体
    *.*.*.*
    链接

    T2噬菌体 2009-04-18 14:12:00

    其实高阶函数,从数学意义上说,就是参变量为一个函数。如
    F(x,y,z),如果令z=f(w),那么F就是一个二阶函数(假设x,y,w都不为函数),依次类推。

    在数学上比较典型的高阶函数就是积分变上限函数。鼎鼎大名Fourier变换其实也是一种高阶函数变换。

    如果了解了这个意义,推广到程序设计语言中,本质也是一样的。就是高阶函数参数可以是另一个函数,这样,就需要对函数进行一个体征抽象,而委托就是这么一种抽象。所以,C#中将委托和高阶函数结合再合适不过了。

  13. 老赵
    admin
    链接

    老赵 2009-04-18 15:01:00

    @T2噬菌体
    简单地说,函数要有成为一等公民的能力

  14. T2噬菌体
    *.*.*.*
    链接

    T2噬菌体 2009-04-18 15:06:00

    @Jeffrey Zhao
    楼上正解,我顶

  15. 上升高度![未注册用户]
    *.*.*.*
    链接

    上升高度![未注册用户] 2009-04-18 15:37:00

    上升到了高度!学习。。。。。

    老赵加油!!

  16. 老赵
    admin
    链接

    老赵 2009-04-18 15:49:00

    @上升高度!
    真上升高度了?我觉得至少有一半人的上升高度是因为开始装b了,不是好事啊……

  17. OwnWaterloo[未注册用户]
    *.*.*.*
    链接

    OwnWaterloo[未注册用户] 2009-04-18 16:22:00

    ----------------------------------------
    且匿名类的内部逻辑无法修改调用方法里的局部变量——由此也可对比出C#中匿名函数这一特性的美妙之处。
    ----------------------------------------

    那么, 对调用方法中的局部变量, C#是怎么处理的? 怎么传递的?
    对匿名delegate, 这个是叫captured local variable吧?
    会将auto local variable 提升为 static local variable, 这并不一定是程序员希望的。 也就是说, 这不一定特别美妙。

    那对Func也属于delegate? 那么处理应该同。
    它与delegate的关系如同binary_function 之于STL?


    还有 lambda表达式又是如何处理被捕获的局部变量的呢???



    没有别的意思, 因为这里其实很有可能有暗礁。
    作为一个负责的作者至少应该提示一下, 而不是说它美妙什么的。

  18. 老赵
    admin
    链接

    老赵 2009-04-18 16:33:00

    @OwnWaterloo
    美妙也不代表没有缺点,滥用误用总归是不好的。文章关注点不同篇幅有限,有些东西自然就忽略了,您说的这些我在其他文章里其实都谈论过(还有一些内容我并不明白您在谈什么,呵呵)。
    不过您说的也有道理,下次我会注意的,争取成为一个“负责的作者”。

  19. Colin Han
    *.*.*.*
    链接

    Colin Han 2009-04-18 17:17:00

    @OwnWaterloo
    @Jeffrey Zhao
    呵呵,是啊。最典型的坑就是那个循环里面包含匿名函数去使用循环变量的问题。
    我是疼过,所以,我的项目里面是非常不推荐使用匿名方法的。

  20. OwnWaterloo[未注册用户]
    *.*.*.*
    链接

    OwnWaterloo[未注册用户] 2009-04-18 17:58:00

    @Jeffrey Zhao
    @Colin Han
    其实我不是做.Net的 …… 不过我很喜欢C#语言。
    确实如老赵所说, java是一潭死水, C#的设计者才是真正在为程序员着想, 提高程序员的生产力。

    而我暂时也想不出例子来, 楼上的Colin Han好像有实际经历, 他也许有实际例子的例子, 说明有时候被捕获的局部变量会让程序员吃惊。

    请教下Conlin Han, 如果使用lambda表达式, 能解决被捕获的局部变量问题么?
    匿名委托不能解决这个问题是因为: 它不能“显式”说明使用局部变量的方式, 所以它只好将局部变量弄成静态的。

    那lambda表达式呢?
    它使用一个参数来使用局部变量?
    那它应该有能力表达:对局部变量是采取拷贝还是引用的方式,是吗? C#lambda表达式语法我不熟……

  21. 老赵
    admin
    链接

    老赵 2009-04-18 20:31:00

    @OwnWaterloo
    匿名委托就是lambda表达式,完全等价,两种不同表现方法而已。我觉得你一直想着“引用”“拷贝”是在把简单问题复杂化。使用匿名方法,只是由编译器产生了一个“类闭包”而已,很容易理解。
    关于产生的问题,我这里提过一点:http://www.cnblogs.com/JeffreyZhao/archive/2009/03/13/anonymous-method-false-sharing.html
    这样的问题也很容易解决,显式声明变量即可,所以放心使用便是。

  22. 老赵
    admin
    链接

    老赵 2009-04-18 20:33:00

    @Colin Han
    你把那么强大的特性给“不推荐”了实在可惜。
    其实这样的问题,完全可以让静态检查工具提出warning,不会有问题的。
    而且我不觉得有太多问题,我是大量使用匿名方法的,呵呵。

  23. redmoon
    *.*.*.*
    链接

    redmoon 2009-04-18 20:51:00

    @Colin Han
    不能因为有缺陷就否定优点。并且这种缺陷是可以避免的。
    现在,没有Lambda我就不会写程序了。

  24. kyorry
    *.*.*.*
    链接

    kyorry 2009-04-18 20:57:00

    InfoQ经常性的打不开

  25. 老赵
    admin
    链接

    老赵 2009-04-18 21:11:00

    --引用--------------------------------------------------
    redmoon: @Colin Han
    不能因为有缺陷就否定优点。并且这种缺陷是可以避免的。
    现在,没有Lambda我就不会写程序了。
    --------------------------------------------------------
    我也是,呵呵

  26. xjb
    *.*.*.*
    链接

    xjb 2009-04-18 23:44:00

    如果也把这些也用java来实现,有比较也许更有说服力

  27. 郑晖
    *.*.*.*
    链接

    郑晖 2009-04-18 23:49:00

    楼主:

    >>Java是一个固步自封的语言
    Java固步自封?Java语言从1.0到即将的7.0,每次都有新的语法特征加入。
    C#作为后辈,起点高,对函数式、动态类型等的支持是Java所没有的,但不代表Java今后就不支持。此外,Java的小兄弟Groovy也是动态语言,与Java很容易集成。其他的如JRuby、Jython与Java的集成也不难。
    要知道,Java有更多的历史包袱,动辄在语法上作大改动,会牵连到大批用户。这是成熟和流行的代价。想想c++吧,十年还没有出一个新标准呢。

    另外,微软一直不肯提供C#在Windows平台之外的支持,算不算固步自封呢?

    >>开发效率之低已经是难以辩驳的事实
    Java开发效率虽然比Ruby、Python低,但有丰富的类库、框架和开发工具,更何况Java语言更加严谨,程序员的风格较为一致,减少了许多可能的犯错机会。综合起来,其效率真的会比C#低吗?“难以辩驳的事实”是从什么数据得来的?

    >>最简单验证方法,便是尝试着去写一点Java程序,会发现很多时候寸步难行
    太夸张了吧?即使用C和C++,只要对语言足够的熟悉,也不致于寸步难行。若果真Java如此难用,Java早就被淘汰了,不劳你唱衰了。

    >>你可以说ruby某个功能1行就能做,而C#要5行,那么我可以补充一个类库,完成1 行的功能。而Java就做不到

    为什么Java就做不到呢?能说个理由吗?

    >>这也是为什么现在不少编程语言的书,都喜欢和Java做对比,因为Java的生产力的确是“难以提高”
    这个说法十分站不住脚。什么语言都与Java比,那是因为Java是当今最流行的语言。等C#在流行度超过了Java,人们自然会找C#比。

    ============================================================
    楼主喜欢C#是你的事情,犯不着一而再、再而三地贬低Java吧?没有人反驳你,是因为园子里大多以.net技术为主,对Java不一定熟悉,而你在这里似乎又是个权威。但那不代表你说的都是正确的。本来你关起门来自说自话,我也不会费神去反驳。但既然放在首页,总要经得起推敲吧?尤其你还是个在园子里有影响的人,说话更要严谨。
    楼主听惯了太多的恭维话,我的话想必有些刺耳。先别急着反驳我,心平气和地想一想。一个好的程序员,应该有一颗开放的心,任何语言、任何技术都可为我所用,不带偏见,不拘一格。

  28. 老赵
    admin
    链接

    老赵 2009-04-19 00:13:00

    @郑晖
    我评论的是Java,不是JVM上的语言,Java是有spec的,我不想关心Java固步自封的原因,我只是想说,Java的生产力太低。微软不让C#跨平台是策略原因,不是固步自封。
    Java的确有丰富的类库框架,我也承认,我只是一直在说Java语言特性。
    如果你要证明Java的生产力高,不妨可以用C#和Java做同样的事情试试看,不用作比较。例如,你用Java试试看实现我现在这篇文章的内容,作的到吗?我的文章最后也用Java实现了,结果呢?
    你说好的程序员要有一颗开放的心,没错我同意,我正是这样的,因此我一直在设法同时使用微软和开放平台,一直提倡混合编程,也一直了解Java/Ruby/Python等语言。但是,兼容并包并不代表寻求平衡,微软对框架的滥用,我也批评,对Java的批评也是基于事实,不是偏见。贬低Java也是因为Java自身不争气,因为它不好,我为什么不能说?
    我说的话都绝对负责,经得起推敲,除非“偷懒”都非常严谨。
    而你现在呢?除了说一些“道理”之外,又有没有实例呢?我说的都是有实例的。

  29. 郑晖
    *.*.*.*
    链接

    郑晖 2009-04-19 04:20:00

    @Jeffrey Zhao
    >>我不想关心Java固步自封的原因,我只是想说,Java的生产力太低。微软不让C#跨平台是策略原因,不是固步自封

    首先你就没有证明“Java固步自封”的结论,而我已经提到Java的语法也在逐步演进,你能否认吗?我还提到Java因为历史相对C#较长,使用的人更多,不可能作激烈的演变。如果你坚持认为这是一种“固步自封”,并且也不关心背后的原因,那为什么又说“微软不让C#跨平台是策略原因”?怎么这时候又关心起C#封闭的背后原因了?难道Java演进较慢就不是策略原因吗?这不是双重标准么?(不跨平台难道不是一种封闭的表现?想想有那么多基于unix/linux的服务器。)

    >>如果你要证明Java的生产力高,不妨可以用C#和Java做同样的事情试试看,不用作比较。例如,你用Java试试看实现我现在这篇文章的内容,作的到吗?

    Java的生产力可不是一两段toy代码就能说明问题的,更何况代码更短不一定代表生产力就更高,还要考虑出错率、可读性、可维护性、可扩展性等等问题。如果你这么强调生产力,为什么不用生产力更高的Ruby、Python呢?此外,你提到的文章侧重于函数式风格,那恰是Java支持不力之处。问题是,究竟在一个大型OOP系统中,函数式的使用究竟有多大比例?再者,虽然类库、框架不属于语言,但语言的生产力难道仅仅是孤立的语法本身所提供的吗?

    >>对Java的批评也是基于事实,不是偏见。

    基于事实?带上了有色眼镜的人,看到的事实未必真的是事实。

    >>贬低Java也是因为Java自身不争气,因为它不好,我为什么不能说?

    Java争不争气,仁者见仁,智者见智。你认为它不好,那也是你个人的观点,我可不会无聊到和别人争这种问题。但你逮到机会就贬低它(看看你对别人的回复就知道了),这次更在首页中妄作偏激之论,实在令人看不过眼。以你在园子里的影响力,不知会误导多少人。如果不是这个原因,我才懒得到他人府上说些逆耳之言。
    试问,如果Java不这么流行,如果Java不是C#是竞争对手,你还会这么贬低它吗?C++生产力更低,十年了语法还是不变,怎么不见你贬低它?假如Java真的那么不好,会那么流行吗?C#会去借鉴它么?没有Java,C#会是今天这个样子吗?
    微软的MVP的一个职责就是充当微软的吹鼓手吧?吹微软的技术可以啊,一定要弹Java而后快吗?Java社区很少人去攻击.NET,但中国的.NET社区似乎比微软更恨Java,真是滑稽啊!

    >>我说的话都绝对负责,经得起推敲,除非“偷懒”都非常严谨。
    你可以申明对自己的话负责,但自己说自己的话“经得起推敲”,“非常严谨”,自我感觉未免过于良好了吧?
    ——“尝试着去写一点Java程序,会发现很多时候寸步难行”
    如果你这样一位高手都有这种感觉,那广大的Java程序员岂不是痛苦死了?

    ——“ruby某个功能1行就能做,而C#要5行,那么我可以补充一个类库,完成1 行的功能。而Java就做不到”
    Java就不能补充类库了么?

    这些都是经得起推敲的话吗?

    >>而你现在呢?除了说一些“道理”之外,又有没有实例呢?
    难道反驳你的观点靠讲道理还不够吗?要我举实例?往一个盛满水的杯子里继续倒水的蠢事,我是做不出来的。从你那么快就回复我的上一个评论,就可看出你是不准备接受什么的了。我费事再度发言,与其是对你说的,不如说是对你的读者说的。我只想让园子里多听到一种声音。


    PS: C#的一些语法特性我也很欣赏,也别以为我是Java的粉丝。我不是任何语言的粉丝,如果一定要找最喜欢的语言,那也不是Java。

  30. 老赵
    admin
    链接

    老赵 2009-04-19 04:26:00

    http://www.cnblogs.com/JeffreyZhao/archive/2009/04/19/why-i-do-not-like-java.html
    看看这里,发现你的疑问我已经全部回答了,呵呵。

  31. 老赵
    admin
    链接

    老赵 2009-04-19 04:27:00

    引用:微软的MVP的一个职责就是充当微软的吹鼓手吧?吹微软的技术可以啊,一定要弹Java而后快吗?Java社区很少人去攻击.NET,但中国的.NET社区似乎比微软更恨Java,真是滑稽啊!
    -----------------------------------------------
    正好相反吧,这里好像也就我一直在恨Java,要不您再找一个出来?而.NET技术是一直很可怜地被打压的,呵呵。还有好像微软MVP说微软好就是假的,说微软坏才是真的,呵呵,您的逻辑很不客观啊。我批评微软的时候您没有看到吗?

  32. 老赵
    admin
    链接

    老赵 2009-04-19 04:35:00

    引用:试问,如果Java不这么流行,如果Java不是C#是竞争对手,你还会这么贬低它吗?C++生产力更低,十年了语法还是不变,怎么不见你贬低它?假如Java真的那么不好,会那么流行吗?C#会去借鉴它么?没有Java,C#会是今天这个样子吗?
    -----------------------------------------------------------
    是啊,Java流行是我贬低它的必要条件,但是同样流行的Ruby、Python、JavaScript我就不贬低,因为它们能力强,我贬低的是Java的能力,不是Java的流行。如果Java不是C#的竞争对手,如果它够流行够弱那么我当然还会贬低它。
    C++我不多作评论那是因为我对它的了解实在不够多,说出来怕被人笑话,要负责么,呵呵。我对java的了解够多,不怕吹。C#是借鉴了Java,但是C#会发展,Java为啥不发展?Java对于C#的作用,是Java固步自封的理由吗?

  33. 老赵
    admin
    链接

    老赵 2009-04-19 04:41:00

    引用:Java的生产力可不是一两段toy代码就能说明问题的,更何况代码更短不一定代表生产力就更高,还要考虑出错率、可读性、可维护性、可扩展性等等问题。如果你这么强调生产力,为什么不用生产力更高的Ruby、Python呢?此外,你提到的文章侧重于函数式风格,那恰是Java支持不力之处。问题是,究竟在一个大型OOP系统中,函数式的使用究竟有多大比例?再者,虽然类库、框架不属于语言,但语言的生产力难道仅仅是孤立的语法本身所提供的吗?
    ------------------------------------------
    说地好似C#的能力消弱了“出错率、可读性、可维护性、可扩展性”一样,呵呵,您的逻辑无法让人信服。我不用ruby、python是因为我不觉得他们带来多大生产力了,除非合适的时候我采取用,您没有看到我怎么结合ASP.NET和IronPython的吗?
    还有,您去看看Ruby、Python就知道函数式风格在项目中有多么重要了,有多大比例。
    当然,类库的确也很影响生产力,但是您不要偷换概念,我一直在说Java语言不是Java平台,类库数量难道就是Java不思进取固步自封的理由了吗?

  34. 老赵
    admin
    链接

    老赵 2009-04-19 04:42:00

    引用:不跨平台难道不是一种封闭的表现?想想有那么多基于unix/linux的服务器
    -------------------------------------------
    mono不是做的好好的吗?
    再者,您又偷换概念了,“封闭”和“固步自封”是两码事,一个是不思进取,一个是策略——当然我不会觉得这是一种好事,呵呵。

  35. MicroCoder
    *.*.*.*
    链接

    MicroCoder 2009-04-19 10:19:00

    文章中:“.NET 3.5中甚至定义了三种泛化的委托类型:Action<>、Predicate<>以及Func<>” 。
    Action<>、Predicate<>以及Func<>应该是在 .NET Framework 2.0 版中是新增的

  36. 郑晖
    *.*.*.*
    链接

    郑晖 2009-04-19 11:46:00

  37. 老赵
    admin
    链接

    老赵 2009-04-19 12:55:00

    @MicroCoder
    的确是3.5,2.0里只有Action没有Action<T>而且没有Predicte和Func——印象中,很深刻。

  38. 装配脑袋
    *.*.*.*
    链接

    装配脑袋 2009-04-20 11:06:00

    http://www.cnblogs.com/Ninputer/archive/2005/11/11/273627.html

    这篇文章发布在VS2005(注意是2005)正式版之前。看看里面的代码就知道Predicate,甚至是Func的渊源了:P

  39. 老赵
    admin
    链接

    老赵 2009-04-20 11:12:00

    @装配脑袋
    你自己的改造版本?.NET 2.0里还是没有吧……
    不过由此可见,脑袋果然是强人……

  40. OwnWaterloo
    *.*.*.*
    链接

    OwnWaterloo 2009-04-20 13:56:00

    --引用--------------------------------------------------
    Jeffrey Zhao: @OwnWaterloo
    我觉得你一直想着“引用”“拷贝”是在把简单问题复杂化。使用匿名
    方法,只是由编译器产生了一个“类闭包”而已,很容易理解。
    --------------------------------------------------------

    嗯, 就是你文章里说的那个问题。
    因为我平时用C++比较多, 所以对变量生命周期很在意。

    delegate void F0();
    void test() {
    int i = 0;
    F0 f = null;

    f = null;
    for (i = 0;i<2;++i)
    f += delegate() { Console.Write(i + " "); };
    f(); // 输出2 2
    Console.WriteLine();

    f = null;
    for (i=0;i<2;++i) {
    int j = i;
    f += delegate() { Console.Write(j + " "); };
    }
    f(); // 输出 0 1
    Console.WriteLine();
    }

    我觉得这是匿名委托一个很不清晰的地方。
    它没有显式说明对局部变量是采取拷贝还是引用语意。
    只有碰见让人惊讶的代码之后, 才知道他使用的是引用语意。

    但是, 说他是引用, 还是有点蹊跷。
    int是值类型。 那个j在每次循环结束后, 都是要被销毁的。

    那个i, 在test返回后, 也是如此, 但是如果将f传递出test, 依然可以引用这个i。

    .Net如何处理i、j的生命周期?
    对于i, 可以将i提升到静态区去。
    对于j, 就不行了。(把上述代码的for循环次数改为运行时才知道,依然正常工作), 所以可能是采用缓式拷贝:
    闭包引用i、j, 在i、j即将被销毁前,拷贝i、j的值。

    以上全是猜测。 看不懂IL代码 ……

    ----------------------------------------
    在C++0x中, lambda表达式很清晰的说明如何使用局部变量:
    1. 默认采取值拷贝
    2. 需要引用的时候, 必须显式声明

    boost::signal<void ()> f;
    for (int i=0;i<2;++i)
    f.connect( <>() => { std::cout<<i<<" "; }; // 立即拷贝i的值
    f(); // 输出 0 1

    boost::signal<void ()> f;
    int sum = 0;
    for (int i=0;i<2;++i)
    f.connect( <>() extern(sum) => { sum+=i; }
    // 立即拷贝 i的值, 对sum采取引用语意
    f();
    std::cout<<sum; //输出3



    --引用--------------------------------------------------
    Jeffrey Zhao: @OwnWaterloo
    匿名委托就是lambda表达式,完全等价,两种不同表现方法而已。
    --------------------------------------------------------

    是啊, 我本来以为C#引入lambda表达式是为了更清晰的说明引用局部变量的方式的。 结果不是。

    昨天看了看, 和匿名delegate相比, 除了写法简单一点, 功能好像是等价的。

    有个什么lambda和LinQ的结合? LinQ我不是太关心, 所以就不知道了……
    也许只有lambda可以, delegate不行?

  41. OwnWaterloo
    *.*.*.*
    链接

    OwnWaterloo 2009-04-20 13:59:00

    有两行写错了……

    1.
    f.connect( <>() extern(sum) => { sum+=i; }
    f.connect( <>() extern(sum) => { sum+=i; } );
    少了尾巴, 不过本来就是伪代码 ……
    C++0x的lambda表达式采用何种语法还没定下来。也有如下比较简洁的建议:f.connect( <>() (&sum) => { sum+=i; } );

    2.
    std::cout<<sum; //输出3
    std::cout<<sum; //输出1
    计算错误 ……

  42. 老赵
    admin
    链接

    老赵 2009-04-20 15:50:00

    @OwnWaterloo
    如果i,j被外部访问了,那么就再也不是局部变量,而是一个对象的成员,对象被传递来传递去,这个对象可以简单理解为一个闭包。
    lambda可以认为就是一个类型推断的表达式而已,如果要生成ExpressionTree,那么只能用lambda而不能用delegate。

  43. OwnWaterloo
    *.*.*.*
    链接

    OwnWaterloo 2009-04-20 17:45:00

    --引用--------------------------------------------------
    Jeffrey Zhao: @OwnWaterloo
    如果i,j被外部访问了,那么就再也不是局部变量,而是一个对象的成员,对象被传递来传递去,这个对象可以简单理解为一个闭包。
    --------------------------------------------------------
    1. 每个匿名delegate或者lambda表达式, 生成一个闭包。
    2. 每个被引用的局部变量, 生成一个闭包。

    .Net采取的是2? 那暂时没有什么矛盾了。
    因为我的C++背景, 误认为是采取1的方式。


    那在
    http://www.infoq.com/cn/articles/higher-order-function
    这篇文章中, 给出到
    http://www.cnblogs.com/JeffreyZhao/archive/2009/03/13/anonymous-method-false-sharing.html
    这个的链接? 如何?

    我觉得这是一个…… 容易造成困扰的地方 ……
    即使是C#背景的人也难免中招。


    --引用--------------------------------------------------
    Jeffrey Zhao: @OwnWaterloo
    lambda可以认为就是一个类型推断的表达式而已,如果要生成ExpressionTree,那么只能用lambda而不能用delegate。
    --------------------------------------------------------
    ExpressionTree就不懂了 ……


    btw: 再请教一个问题^_^
    有办法订阅博客园的评论么?
    我有cppblog, 可以订阅其他cppblog上的回复。
    但是不能订阅cnblogs…… 虽然他们是一家的……

    又不想注册一个空的cnblogs,浪费博客园资源……

  44. 老赵
    admin
    链接

    老赵 2009-04-20 17:48:00

    @OwnWaterloo
    .net其实是看情况,如果i和j的作用域完全相同,那么就是放在同一个对象上啊。
    我不知道能不能订阅回复,我去问问吧……

  45. 啊啊[未注册用户]
    *.*.*.*
    链接

    啊啊[未注册用户] 2009-05-08 12:50:00

    @OwnWaterloo
    法十分

  46. Fergon
    14.208.153.*
    链接

    Fergon 2012-02-23 03:21:35

    @T2噬菌体

    其实高阶函数,从数学意义上说,就是参变量为一个函数。如 F(x,y,z),如果令z=f(w),那么F就是一个二阶函数(假设x,y,w都不为函数),依次类推。

    在学习c++的运算符结合性时,同时要考虑到表达式的副作用;往往容易感到困惑。

    1+2+3+5是否也算是一个高阶函数?

    我觉得这个算式就是一个二元计算的递归过程:

    +(5,+(3 ,(+(2,1))))

    这样看过去,结合性就一目了然;

    涉及到副作用的算式:

    cout << a++ << a+5;

    大约是这个样子吧:

    cout(cout(this,a++), a+5)

    我把副作用的规则归结为参数的求值顺序未定义(除去特殊的几个),不知你有什么看法?

    所以我觉得,结合性、副作用都是相对于高阶函数而言的。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我