思立's profile我不是柠檬BlogListsGuestbookMore Tools Help

Blog


    February 29

    看艳照,学英文(zt)

    其实我觉得文章做阅读理解差了点吧,多数用词还是标准而普通的,当然也可能是我英文太差的缘故.

    作者:新东方 杨政
        
    前言:真的没想到陈冠希会用全英文做道歉陈述。他500多字的道歉信, 用词准确地道,词汇难度较高,包含了50多个考研(雅思)核心词汇,经过改编完全可以成为一篇高质量的完形填空,或阅读理解。Edison这小子虽然"猥 琐",但英文用词的难度达到了考研英语(国内最变态英语考试)的要求,如果参加雅思考试估计8分以上不成问题。陈已经决定推出香港娱乐圈,对于这样优秀的 英语人才(YZ门暂且不谈),老俞是不是可以考虑挖到新东方来呢?估计轰动效应不亚于去年Sister Lotus(芙蓉姐姐) 参加新东方新概念二册的培训。
      
    视屏推荐: http://v.youku.com/v_playlist/ca00f1380309o9p0.html
      
    推荐理由: 同传口译者不仅翻译准确及时,而且非常投入,感情充沛,感觉就像是自己经历了YZ门.
      
    Today I have come back to Hong Kong to stand before you and account for myself. I have never escaped from my responsibility. During the past few weeks, I have been with my mother and my family and my loved ones to show support and care and at the same time to have them support and care for me.
      
    核心词汇解析
      
    account for 1) 说明,解释;2) 占…比例
      
    escape from从…逃脱, 推卸责任
      
    escape=es(ex=out) + cape(catch)
      
    I admit that most of the photos being circulated on the Internet were taken by me. But these photos are very private and have not been shown to people and are never intended to be shown to anyone. These photos were stolen from me illegally and distributed without my consent.
      
    核心词汇解析
      
    circulate = circul(circle圆圈) + ate v.循环,流通
      
    intend to v.打算去…
      
    illegal = il(not) + legal (合法的) + ly adv.违法地
      
    单词组记: distribute, contribute, attribute, tribute
      
    distribute = dis(away)+ tribute(give) = give away vt.分发,传播
      
    contribute = con(fully) + tribute vt.贡献; 投稿 [助记]全部都给了
      
    attribute to v.归因于
      
    tribute n.贡品,颂词 [助记] 给国王的礼物
      
    单词组记:consent, dissent, resent, sentimental
      
    consent = con(共同)+ sent(=sense情感) n.赞同,同意=agreement
      
    dissent = dis(not) + sent n.不同意 [助记]不同的情感 = disagreement
      
    resent = re(against) + sent n.憎恨 [助记] 相对抗的情感= hatred
      
    sentimental[谐音]三屉馒头,失恋了只吃三屉馒头,所以是---a.多愁善感的
      
    There is no doubt whoever obtained these photos have them uploaded on the Internet with malicious and deliberate intent. This matter has deteriorated to the extent that society as a whole has been affected by this. In this regard, I am deeply saddened. I would like now to apologize to all the people for all the suffering that has been caused and the problems that have arisen from this. I would like to apologize to all the ladies and to all their families for any harm or hurt that they have been feeling. I am sorry. I would like to also apologize to my mother and my father for the pain and suffering I have caused them during the past few weeks. Most importantly, I would like to say sorry to all the people of Hong Kong. I give my apology sincerely to you all, unreservedly and with my heart.
      
    核心词汇解析
      
    upload = up + load vt.上传(图片, 文件等)
      
    download = down + load vt.下载(图片,文件等)
      
    单词组记: mal-=bad坏
      
    malice = mal + ice(冰) n.恶意;【律】预谋, 蓄意 [助记]在路上放一块冰,想让陈冠希滑倒,这是有预谋的恶意伤害。
      
    malicious= malice(恶意) + ious(形容词) a.恶意的
      
    mal-function n.功能紊乱
      
    mal-nutrition n.营养不良
      
    maltreat = mal + treat(对待) vt.虐待
      
    单词组记: libra天平, deliberate, liberate, liberty, libertine, libido
      
    一直到现在,英国的法院门口还站立着古希腊象征法律精神的正义女神(DICE)的雕像,她左手拿着神圣的天平Libra,象征着权衡和平等;右手拿着宝剑,象征着裁决和力量;眼睛被布蒙着,象征绝对的公正无私。
      
    deliberate = de(强调) + libera(=libra) + ate a. 深思熟虑的, 故意的 [助记] 把你的想法放在天平上称量,引申为深思熟虑的,深思熟虑的结果当然是故意的
      
    liberate = liber (自由=libera)+ ate vt.解放(让人民获得自由)
      
    自由和天平有什么关系呢?
      
    天平的主要作用就是要让两边重量相等,平等。解放全人类不就是让人民平等吗?人民解放军PLA:People’s Liberation Army liberation的职责就是解放全中国让中国人民平等自由
      
    liberty n.自由
      
    libertine这个单词叫"浪荡子",原来就是“特别自由的人”的意思,” 挺(tin)自由(liber)的”
      
    libido弗洛伊德的著作里面,他造出一个表示“性动力”的单词libido(中文翻译成“利比多”),表达的就是这种本源的不受控制的力量。
      
    单词组记:deteriorate,interior, exterior, territory
      
    deteriorate = de(down) + terior(土地) +ate(动词) v.使恶化; 败坏(风俗); 使变坏(品质等) [助记] 品格败坏的人就应该入土活埋。
      
    interior = in(into) + terior(土地) a.内部的,国内的
      
    interior design 室内设计专业
      
    exterior = ex (out)+ terior(土地) a.外部的,国外的
      
    territory = territ(=terior)+ ory(地方) n.版图,疆土
      
    to the extent 到了….程度
      
    in this regard 在这一点上 = in this case
      
    单词组记: 神奇的-en可以把形容词名词变为你想要的动词
      
    sadden = sad + d + en vt.使人悲哀
      
    strengthen = strength(strong的名词) + en vt.加强(力量)
      
    weaken = weak + en vt.削弱
      
    lengthen = length(long的名词) + en vt.延长
      
    shorten = short + en vt.缩短
      
    tighten = tight + en vt.拉紧
      
    loosen = loose + en vt.放松
      
    还有一个超级牛的单词前后都加en
      
    enlighten= en + light + en vt.启蒙;(用思想)照亮
      
    intent n.意图,打算
      
    unreservedly = un + reserved(保留的) + ly(副词) adv.毫无保留地
      
    I know young people in Hong Kong look up to many figures in our society. And in this regard, I have failed. I failed as a role model. However, I wish this matter will teach everyone a lesson. To all the young people in our community, let this be a lesson for you all. This is not an example to be set for you.
      
    核心词汇解析
      
    look up to 仰望,尊重= respect
      
    look down upon/on 鄙视,瞧不起=despise
      
    role model 榜样
      
    During my time away, I have made an important decision. I will whole-heartedly fulfill all commitments that I have to date. But after that, I decided to step away from the Hong Kong entertainment industry. I have decided to do this to give myself an opportunity to heal myself and to search my soul. I will dedicate my time to charity and community work within the next few months. I will be away from Hong Kong entertainment industry indefinitely. There is no time frame.
      
    核心词汇解析
      
    whole-heatedly 全心全意地; single-mindedly一心一意地
      
    fulfill one's commitment vt.履行…的诺言
      
    entertainment industry 娱乐业
      
    health =heal(恢复健康) +th(名词)
      
    heal (v.治愈,恢复) 是health的反向构词
      
    dedicate to 把...奉献给,投身于...
      
    charity = char(=care关心)+ ity(名词) n.慈善,慈善事业 [助记]关心穷人就是做慈善
      
    I have been assisting the police since the first day the photos were published and I will continue to assist them. After this press con., I have obligation to help them with their investigation and hope that this case can end soon as everyone I think has the same wish.
      
    核心词汇解析
      
    assist = as(to) + sist(stand) vt.(站在一旁来) 协助
      
    press con. = press conference 新闻发布会
      
    press free. = press freedom 言论自由
      
    press 是“压”的意思,怎么会变成“新闻”,“言论”呢?
      
    因为最初的报纸都是油墨印刷,需要把纸紧紧的压在刻版上才能印刷出字来.所以press有了一个引申意:n.出版业,印刷业. 出版印刷业不就是发布新闻和言论的阵地吗?
      
    obligation n.义务
      
    investigation n.调查
      
    I would like to use this opportunity to thank the police for their hard work on this case. Thank you. I believe everyone’s priority now (and) my priority now is to stop the suffering and pain, for not letting this…we do not want to let this situation become more out of control. We need to protect all the innocents and all the young from matters like this. In this regard, I have instructed my lawyers to do everything possible within the law to protect all the innocents, victims of this case. I believe that a press statement is being issued as we speak on what my lawyers have advised me to do.
      
    核心词汇解析
      
    单词组记:prior to, priority
      
    pri = pre是一个拉丁前缀表示before
      
    [书面]prior to = [口语]before
      
    小翻译 : 在你离开之前,请完成这项工作.
      
    prior to your departure, please complete this task.
      
    小翻译: 预先警告/通知 prior warning/notice.
      
    priority = prior + ity(名词) n.优先考虑的事情, 优先权
      
      
    单词组忆: victor, victory, victim
      
    vict中的V象征着胜利,vict-表示征服
      
    victory n.胜利
      
    victor =vict + or(人) n.胜利者, 征服者
      
    victim = vict + im(我是) [助记]我是被征服了,当然就成了n.受害人,牺牲品
      
      
    innocent = in + no + cent(一分钱) [助忆]兜里没有一分钱, 所以没有偷窃, 是无辜的 a. 无辜的, 天真的
      
    instruct = in + struct(构造) vt.传授,告知 [助记]内心中去构建知识或信息
      
    Lastly, I would like to thank everyone for coming here today and listening to what I have to say. I would like to also apologize once again to all the ladies and their families, my family and to everyone in Hong Kong and everyone in our society. I am deeply saddened by this. And I apologize to everyone (who) has to go through this. I would like to also thank you for giving me this opportunity to say what I have wanted to say all along in my heart.
      
    I hope, after today, I can have your forgiveness. With regard to this case, with everything, everything that has happened, I am deeply sorry. I hope you all accept my apology and give me a chance. Thank you.
      
    [书]with regard to关于= [口]about
    February 14

    春节电影记

    第一次不在家过春节,初三才回来.回来之后还是觉得冷,看到街上还有很多地方雪没有化,想想雪灾最惨的时候,还是很可怕的.
    好不容易有机会,可以看了一些电影,这几天一口气看了不少东西.总结一下看完的电影.
    <长江7号>-很真诚的儿童片还是儿童片,而且不是拍得很好的儿童片,星爷人到中年,开始童心复苏,故事却编得不圆很和谐,除了选星女郎的眼力大有提升,其余都比起<功夫>还差.看看由他带出道的张柏芝,已经在陈贯希老师的指导下拍起很黄很暴力的成人片了,真是岁月无情.
    <超级坏>-觉得比<美国派>系列好,那个TankMan化身为dick是让中国人会心一笑的妙笔.忽然发现,美国电影表现的成长通常是从无法无天,荷尔蒙支配大脑的小P孩,成长为懂责任懂爱的成年人的过程,而东方电影(不光是中国的,似乎日本韩国人也爱这么拍)表现的成长通常是从理想主义的激情飞扬的少年转变成世故犬儒的中年人,是不是文化使然.
    <超市夜未眠>-全是文青的范儿啊,从每个镜头,到每句台词,到每个小段落的设置,唯恐不文艺,生怕不唯美.忽然觉得这男猪也真是一正宗的艺术工作者.停止时间啊,多么牛的能力,我从小学起看<恐龙特急客塞号>就梦想拥有的能力.我就是在想像中有了停止时间的能力,也去抢了想像中的银行了,哪还像男猪这样在超市里画人体......
    <星尘>-还可以的魔幻片,主要是我不爱这一口,不过看Robert De Niro穿个女装跳舞,真是太好玩了.
    <神探>-好多人吹捧,喜欢银河印象那口的自然喜欢,不喜欢的还是不喜欢.说实话,这次的设置,一个可以看透人格(特异功能?)的神探,并不怎么好,编起故事来也不是很有意思,看了网友启发,才知道尘埃落定后的换枪,才是全片的重头戏.
    <贝奥武夫>-这种片最好打发时间了,我和我弟一起看的.
    <天使爱美丽>-我终于把这片看全了,感谢上帝,这片真牛,真牛,属于每个普通老百姓都可以看懂并感受到牛逼的巨牛片.
    February 08

    关于老鼠的诗

    国风 鄘风 相鼠
    相鼠有皮,人而无仪!人而无仪,不死何为?
    相鼠有齿,人而无止!人而无止,不死何俟?
    相鼠有体,人而无礼,人而无礼!胡不遄死?
     
    诗经·魏风·硕鼠
      
    硕鼠硕鼠,无食我黍!三岁贯女,莫我肯顾。
    逝将去女,适彼乐土。乐土乐土,爰得我所。
    硕鼠硕鼠,无食我麦!三岁贯女,莫我肯德。
    逝将去女,适彼乐国。乐国乐国,爰得我直。
    硕鼠硕鼠,无食我苗!三岁贯女,莫我肯劳。
    逝将去女,适彼乐郊。乐郊乐郊,谁之永号。
    February 05

    鼠年快乐!

    希望所有的朋友和自己,有个快乐的鼠年.
    February 04

    函数式编程另类指南(zt)

    原文链接:Functional Programming For The Rest of Us
    原文作者:Vyacheslav Akhmechet
    翻译:lihaitao (电邮: lihaitao在gmail.com)
    校对:刘凯清

    程序员拖沓成性,每天到了办公室后,泡咖啡,检查邮箱,阅读 RSS feed,到技术站点查阅最新的文章,在编程论坛的相关版面浏览公共讨论,并一次次地刷新以免漏掉一条信息。然后是午饭,回来后盯了IDE没几分钟,就再次检查邮箱,倒咖啡。最后在不知不觉中,结束了一天。

    不平凡的事是每隔一段时间会跳出一些很有挑战性的文章。如果没错,这些天你至少发现了一篇这类文章——很难快速通读它们,于是就将之束之高阁,直到突然你发现自己已经有了一个长长的链接列表和一个装满了PDF文件的目录,然后你梦想着到一个人迹罕至的森林里的小木屋苦读一年以期赶上,要是每天清晨你沿着那里的林中小溪散步时会有人带来食物和带走垃圾就更好了。

    虽然我对你的列表一无所知,但我的列表却是一大堆关于函数式编程的文章。而这些基本上是最难阅读的了。它们用枯燥的学院派语言写成,即使“在华尔街行业浸淫十年的专家(veterans)”也不能理解函数式编程(也写作FP)都在探讨些什么。如果你去问花旗集团(Citi Group)或德意志银行(Deutsche Bank)的项目经理[1],为什么选择了 JMS 而不 Erlang,他们可能回答不能在产业级的应用中使用学院派语言。问题是,一些最为复杂的,有着最严格需求的系统却是用函数式编程元素写成。有些说法不能让人信服。

    的确,关于函数式编程的文章和论文难于理解,但他们本来不必这么晦涩。这一知识隔阂的形成完全是历史原因。函数式编程的概念本身并不困难。这篇文章可以作为“简易的函数式编程导引”。是一座从我们命令式(imperative)的思维模式到函数式编程的桥梁。去取杯咖啡回来继续读下去吧。可能你的同事很快就会开始取笑你对函数式编程发表的观点了。

    那么什么是函数式编程呢?它怎么产生?它可以被掌握吗(Is it edible)?如果它真如其倡导者所言,为什么没有在行业中得到更广泛的使用?为什么好像只有那些拿着博士学位的人才使用它?最要紧的是,为什么它就 TMD 这么难学?这些 closure, continuation, currying,惰性求值和无副作用等等究竟是些什么东西?没有大学参与的项目怎么使用它?为什么它看上去这么诡异于和我们命令式思想友好,圣洁和亲近的一切的一切?我们将于不久扫清这些疑问。首先让我来解释形成实际生活和学界文章之间巨大隔阂的缘起,简单得像一次公园的散步。

    信步游园

    启动时间机器,我们散步在两千多年以前的一个被遗忘了太久的春季明媚的日子,那是公元前380年。雅典城墙外的橄榄树树荫里,柏拉图和一个英俊的奴隶小男孩朝着学院走去。“天气真好”,“饮食不错”,然后话题开始转向哲思。

    “瞧那两个学生,”为了使问题更容易理解,柏拉图仔细地挑选着用词,“你认为谁更高呢?”
    小男孩看着那两个人站着的水漕说,“他们差不多一样高”。
    柏拉图说:“你的差不多一样是什么意思?”。“我在这里看他们是一样高的,不过我肯定如果走近些就会看出他们高度的差别。”
    柏拉图笑了,他正把这个孩子带到正确的方向。“那么你是说,我们这个世界没有完全的等同了?”
    小男孩想了一会儿回答,“对,我不这样认为,任何事物总有一些区别,即使我们看不到它。”
    这句话非常到位!“那么如果这世上没有完全的相等,你又是如何理解‘完全’相等这个概念的呢?”
    小男孩迷惑得说:“我不知道。”最初尝试着理解数学的本源(nature)时也会产生这种疑惑。

    柏拉图暗示这个世上的万物都只是一个对完美的近似。他还认识到我们即使没有接触到完美但依然可以理解这一概念。所以他得出结论,完美的数学形式只能存在于另一个世界,我们通过和那个世界的某种联系在一定程度上知晓他们。很明显我们不能看到完美的圆,但我们可以理解什么是完美的圆并用数学公式将它表达出来。那么,什么是数学?为什么宇宙可以用数学定理描述?数学可以描述宇宙中的所有现象吗?[2]

    数学哲学是一个很复杂的课题。像大多数哲学学科一样它更倾向于提出问题而不是给出解答。这些意见中很多都循回绕转于一个事实,即数学实际上是一个谜语:我们设置了一系列基本的不冲突的原理和一些可以施加于这些原理的操作规则,然后我们就能堆砌这些规则以形成更复杂的规则。数学家把这种方法叫做“形式系统”或“演算”。如果愿意,我们可以很快写出一个关于 Tetris(译者注:一种通常被称为俄罗斯方块的游戏)的形式系统。实际上,工作中的 Tetris 实现就是一个形式系统,只是被指定使用了个不常见的表现形式。

    人马座的那个生物文明也许不能理解我们的 Tetris 和圆的范式,因为可能他们唯一的感知输入是气味香橙的橘子。他们也许永远不会发现 Tetris 范式,但很可能会有一个圆的范式。我们也可能将无法阅读它,因为我们的嗅觉没有那么复杂,可是一旦我们理解(pass)了那一范式的表示形式(通过这种传感器和标准解码技术来理解这种语言),其底层的概念就可被任何智能文明所理解。

    有趣的是如果从来没有智能文明存在,Tetris 和圆的范式仍然严密合理,只是没有人注定将会发现他们。如果产生了一种智能文明,他就会发现一些形式系统来帮助描述宇宙的规律。但他还是不大可能发现 Tetris 因为宇宙中再没有和它相似的事物。在现实世界中这类无用的形式系统或迷题的例子数不胜数,Tetris 只是其中的一个典型。我们甚至不能确定自然数是否是对客观世界的完整近似,至少我们可以简单的想像一个很大的数它不能用宇宙中任何东西描述,因为它以近乎无穷。

    历史一瞥[3]

    再次启动时间机器,这一次的旅行近了很多,我们回到 1930 年代。大萧条正在蹂躏着那个或新或就的时代。空前的经济下挫影响着几乎所有阶层的家庭生活,只有少数人还能够保持着饥谨危机前的安逸。一些人就如此幸运地位列其中,我们关心的是普林斯顿大学的数学家们。

    采用了歌特式风格设计建造的新办公室给普林斯顿罩上天堂般的幸福光环,来自世界各地的逻辑学家被邀请到普林斯顿建设一个新的学部。虽然彼时的美国民众已难能弄到一餐的面包,普林斯顿的条件则是可以在高高的穹顶下,精致雕凿的木质墙饰边上整日的品茶讨论或款款慢步于楼外的林荫之中。

    阿隆左·丘奇就是一个在这种近于奢侈的环境中生活着的数学家。他在普林斯顿获得本科学位后被邀留在研究生院继续攻读。阿隆左认为那里的建筑实属浮华,所以他很少一边喝茶一边与人讨论数学,他也不喜欢到林中散步。阿隆左是一个孤独者:因为只有一个人时他才能以最高的效率工作。虽然如此,他仍与一些普林斯顿人保持的定期的联系,其中包括阿兰·图灵,约翰·冯·诺依曼,和 kurt Grodel。

    这四个人都对形式系统很感兴趣,而不太留意现实世界,以便致力于解决抽象的数学难题。他们的难题有些共同之处:都是探索关于计算的问题。如果我们有了无限计算能力的机器,哪些问题可以被解决?我们可以使他们自动地得以解决吗?是否还是有些问题无法解决,为什么?不同设计的各种机器是否具有相同的计算能力?

    通过和其它人的合作,阿隆左·丘奇提出了一个被称为 lambda 演算的形式系统。这个系统本质上是一种虚拟的机器的编程语言,他的基础是一些以函数为参数和返回值的函数。函数用希腊字母 lambda 标识,这个形式系统因此得名[4]。利用这一形式系统,阿隆左就可以对上述诸多问题推理并给出结论性的答案。

    独立于阿隆左,阿兰·图灵也在进行着相似的工作,他提出了一个不同的形式系统(现在被称为图灵机),并使用这一系统独立得给出了和阿隆左相似的结论。后来被证明图灵机和 lambda 演算能力等同。

    我们的故事本可以到此结束,我会就此歇笔,而你也将浏览到下一个页面,如果第二次世界大战没有在那时打响。整个世界笼罩在战争的火光和硝烟之中,美国陆军和海军前所未有的大量使用炮弹,为了改进炮弹的精确度,部队组织了大批的科学家持续地计算微分方程以解出弹道发射轨迹。渐渐意识到这个任务用人力手工完成太耗精力后,人们开始着手开发各种设备来攻克这个难关。第一个解出了弹道轨迹的机器是 IBM 制造的 Mark I —— 它重达5吨,有75万个组件,每秒可以完成三次操作。

    竞争当然没有就此结束,1949年,EDVAC(Electronic Discrete Variable Automatic Computer,爱达瓦克)被推出并获得了极大的成功。这是对冯·诺依曼架构的第一个实践实例,实际上也是图灵机的第一个现实实现。那一年好运与阿隆左·丘奇无缘。

    直到1950年代将尽,一位 MIT 的教授John McCarthy(也是普林斯顿毕业生)对阿隆左·丘奇的工作产生了兴趣。1958年,他公开了表处理语言 Lisp。Lisp 是对阿隆左·丘奇的 lambda 演算的实现但同时它工作在冯·诺依曼计算机上!很多计算机科学家认识到了 Lisp 的表达能力。1973年,MIT人工智能实验室的一组程序员开发了被称为Lisp机器的硬件-阿隆左 lambda 演算的硬件实现!

    函数式编程

    函数式编程是对阿隆左·丘奇理论的实践应用。但也并非全部 lambda 演算都被应用到了实践中,因为 lambda 演算不是被设计为在物理局限下工作的。因此,象面向对象的编程一样,函数式编程是一系列理念,而不是严格的教条。现在有很多种函数式编程语言,他们中的大多数以不同方式完成不同任务。在本文中我将就最广泛使用的源自函数式编程的思想作一解释,并将用Java语言举例。(的确,你可以用Java写出函数式的程序如果你有显著的受虐倾向)。在下面的小节中,我将会把Java作为一种函数式语言,并对其稍加修改使它成为一种可用的函数式语言。现在开始吧。

    lambda 演算被设计用来探询关于计算的问题,所以函数式编程主要处理计算,并惊人地用函数来完成这一过程。函数是函数式编程的基本单位,函数几乎被用于一切,包括最简单的计算,甚至变量都由计算取代。在函数式编程中,变量只是表达式的别名(这样我们就不必把所有东西打在一行里)。变量是不能更改的,所有变量只能被赋值一次。用 Java 的术语来说,这意味着所有单一变量都被声明为 final(或 C++ 的 const)。在函数式编程中没有非 final 的变量。

    final int i = 5;
    final int j = i + 3;

    因为函数式编程中所有变量都是 final 的,所以可以提出这样两个有趣的表述:没有必要总是写出关键字 final,没有必要把变量再称为变量。那么现在我们对Java作出两个修改:在我们的函数式 Java 中所有变量默认都是 final的,我们将变量(variable)称为符号(symbol)。

    就此你也许会质疑,用我们新创造的语言还能写出有些复杂度的程序吗?如果每个符号都是不可变更(non-mutalbe)的,那么就无法改变任何状态!其实事实并非完全如此。在阿隆左研究其 lambda 演算时,他并不想将某个状态维护一段时间以期未来对其进行修改。他关注的是对数据的操作(也通常被称为”演算体 caculating stuff”)。既然已被证明lambda演算与图灵机等价,它可以完成所有命令式编程语言能够完成的任务。那么,我们怎么才能做到呢?

    答案是函数式程序能保存状态,只是它并非通过变量而是使用函数来保存状态。状态保存在函数的参数中,保存在堆栈上。如果你要保存某个状态一段时间并时不时地对其进行一些修改,可以写个递归函数。举个例子,我们写个函数来翻转 Java 的字符串。记住,我们声明的每个变量默认都是 final 的。[5]

    String reverse(String arg) {
    if(arg.length == 0) {
    return arg;
    }
    else {
    return reverse(arg.substring(1, arg.length)) + arg.substring(0,1);
    }}

    这个函数很慢因为它不断地调用自己[6],它还也是个嗜内存魔因为要持续分配对象。不过它的确是在用函数式风格。你可能会问,怎么有人会这样写程序?好的,我这就慢慢讲来:

    函数式编程的优点

    你可能会认为我根本无法对上面那个畸形的函数给出个合理的解释。我开始学习函数式编程时就是这么认为的。不过我是错了。有很好的理由使用这种风格,当然其中一些属主观因素。例如,函数式程序被认为更容易阅读。因为每个街区的孩子都知道,是否容易理解在旁观者的眼中,所以我将略去这些主观方面的理由。幸运的是,还有很多的客观理由。

    单元测试

    因为函数式编程的每一个符号都是 final 的,没有函数产生过副作用。因为从未在某个地方修改过值,也没有函数修改过在其作用域之外的量并被其他函数使用(如类成员或全局变量)。这意味着函数求值的结果只是其返回值,而惟一影响其返回值的就是函数的参数。

    这是单元测试者的梦中仙境(wet dream)。对被测试程序中的每个函数,你只需在意其参数,而不必考虑函数调用顺序,不用谨慎地设置外部状态。所有要做的就是传递代表了边际情况的参数。如果程序中的每个函数都通过了单元测试,你就对这个软件的质量有了相当的自信。而命令式编程就不能这样乐观了,在 Java 或 C++ 中只检查函数的返回值还不够——我们还必须验证这个函数可能修改了的外部状态。

    调试

    如果一个函数式程序不如你期望地运行,调试也是轻而易举。因为函数式程序的 bug 不依赖于执行前与其无关的代码路径,你遇到的问题就总是可以再现。在命令式程序中,bug 时隐时现,因为在那里函数的功能依赖与其他函数的副作用,你可能会在和 bug 的产生无关的方向探寻很久,毫无收获。函数式程序就不是这样——如果一个函数的结果是错误的,那么无论之前你还执行过什么,这个函数总是返回相同的错误结果。

    一旦你将那个问题再现出来,寻其根源将毫不费力,甚至会让你开心。中断那个程序的执行然后检查堆栈,和命令式编程一样,栈里每一次函数调用的参数都呈现在你眼前。但是在命令式程序中只有这些参数还不够,函数还依赖于成员变量,全局变量和类的状态(这反过来也依赖着这许多情况)。函数式程序里函数只依赖于它的参数,而那些信息就在你注视的目光下!还有,在命令式程序里,只检查一个函数的返回值不能够让你确信这个函数已经正常工作了,你还要去查看那个函数作用域外数十个对象的状态来确认。对函数式程序,你要做的所有事就是查看其返回值!

    沿着堆栈检查函数的参数和返回值,只要发现一个不尽合理的结果就进入那个函数然后一步步跟踪下去,重复这一个过程,直到它让你发现了 bug 的生成点。

    并行

    函数式程序无需任何修改即可并行执行。不用担心死锁和临界区,因为你从未用锁!函数式程序里没有任何数据被同一线程修改两次,更不用说两个不同的线程了。这意味着可以不假思索地简单增加线程而不会引发折磨着并行应用程序的传统问题。

    事实既然如此,为什么并不是所有人都在需要高度并行作业的应用中采用函数式程序?嗯,他们正在这样做。爱立信公司设计了一种叫作 Erlang 的函数式语言并将它使用在需要极高抗错性和可扩展性的电信交换机上。还有很多人也发现了 Erlang 的优势并开始使用它。我们谈论的是电信通信控制系统,这与设计华尔街的典型系统相比对可靠性和可升级性要求高了得多。实际上,Erlang 系统并不可靠和易扩展,Java 才是。Erlang 系统只是坚如磐石。

    关于并行的故事还没有就此停止,即使你的程序本身就是单线程的,那么函数式程序的编译器仍然可以优化它使其运行于多个CPU上。请看下面这段代码:

    String s1 = somewhatLongOperation1();
    String s2 = somewhatLongOperation2();
    String s3 = concatenate(s1, s2);

    在函数编程语言中,编译器会分析代码,辨认出潜在耗时的创建字符串s1和s2的函数,然后并行地运行它们。这在命令式语言中是不可能的,因为在那里,每个函数都有可能修改了函数作用域以外的状态并且其后续的函数又会依赖这些修改。在函数式语言里,自动分析函数并找出适合并行执行的候选函数简单的像自动进行的函数内联化!在这个意义上,函数式风格的程序是“不会过时的技术(future proof)”(即使不喜欢用行业术语,但这回要破例一次)。硬件厂商已经无法让CPU运行得更快了,于是他们增加了处理器核心的速度并因并行而获得了四倍的速度提升。当然他们也顺便忘记提及我们的多花的钱只是用在了解决平行问题的软件上了。一小部分的命令式软件和 100% 的函数式软件都可以直接并行运行于这些机器上。

    代码热部署

    过去要在 Windows上安装更新,重启计算机是难免的,而且还不只一次,即使是安装了一个新版的媒体播放器。Windows XP 大大改进了这一状态,但仍不理想(我今天工作时运行了Windows Update,现在一个烦人的图标总是显示在托盘里除非我重启一次机器)。Unix系统一直以来以更好的模式运行,安装更新时只需停止系统相关的组件,而不是整个操作系统。即使如此,对一个大规模的服务器应用这还是不能令人满意的。电信系统必须100%的时间运行,因为如果在系统更新时紧急拨号失效,就可能造成生命的损失。华尔街的公司也没有理由必须在周末停止服务以安装更新。

    理想的情况是完全不停止系统任何组件来更新相关的代码。在命令式的世界里这是不可能的。考虑运行时上载一个Java类并重载一个新的定义,那么所有这个类的实例都将不可用,因为它们被保存的状态丢失了。我们可以着手写些繁琐的版本控制代码来解决这个问题,然后将这个类的所有实例序列化,再销毁这些实例,继而用这个类新的定义来重新创建这些实例,然后载入先前被序列化的数据并希望载入代码可以恰到地将这些数据移植到新的实例。在此之上,每次更新都要重新手动编写这些用来移植的代码,而且要相当谨慎地防止破坏对象间的相互关系。理论简单,但实践可不容易。

    对函数式的程序,所有的状态即传递给函数的参数都被保存在了堆栈上,这使的热部署轻而易举!实际上,所有我们需要做的就是对工作中的代码和新版本的代码做一个差异比较,然后部署新代码。其他的工作将由一个语言工具自动完成!如果你认为这是个科幻故事,请再思考一下。多年来 Erlang工程师一直更新着他们的运转着的系统,而无需中断它。

    机器辅助的推理和优化

    函数式语言的一个有趣的属性就是他们可以用数学方式推理。因为一种函数式语言只是一个形式系统的实现,所有在纸上完成的运算都可以应用于用这种语言书写的程序。编译器可以用数学理论将转换一段代码转换为等价的但却更高效的代码[7]。多年来关系数据库一直在进行着这类优化。没有理由不能把这一技术应用到常规软件上。

    另外,还能使用这些技术来证明部分程序的正确,甚至可能创建工具来分析代码并为单元测试自动生成边界用例!对稳固的系统这种功能没有价值,但如果你要设计心房脉冲产生器 (pace maker)或空中交通控制系统,这种工具就不可或缺。如果你编写的应用程序不是产业的核心任务,这类工具也是你强于竞争对手的杀手锏。

    高阶函数

    我记得自己在了解了上面列出的种种优点后曾想:“那都是非常好的特性,可是如果我不得不用天生就不健全的语言编程,把一切变量声明为
    final 产生的代码将是垃圾一堆。” 这其实是误解。在如Java 这般的命令式语言环境里,将所有变量声明为 final 没有用,但是在函数式语言里不是这样。函数式语言提供了不同的抽象工具它会使你忘记你曾经习惯于修改变量。高阶函数就是这样一种工具。

    函数式语言中的函数不同于 Java 或 C 中的函数,而是一个超集——它有着 Java 函数拥有的所有功能,但还有更多。创建函数的方式和 C 中相似:

    int add(int i, int j) {
    return i + j;
    }

    这意味着有些东西和同样的 C 代码有区别。现在扩展我们的 Java 编译器使其支持这种记法。当我们输入上述代码后编译器会把它转换成下面的Java代码(别忘了,所有东西都是 final 的):

    class add_function_t {
    int add(int i, int j) {
    return i + j;
    }
    }

    add_function_t add = new add_function_t();

    这里的符号 add 并不是一个函数。这是一个有一个成员函数的很小的类。我们现在可以把 add 作为函数参数放入我们的代码中。还可以把它赋给另一个符号。我们在运行时创建的 add_function_t 的实例如果不再被使用就将会被垃圾回收掉。这些使得函数成为第一级的对象无异于整数或字符串。(作为参数)操作函数的函数被称为高阶函数。别让这个术语吓着你,这和 Java 的 class 操作其它 class(把它们作为参数)没有什么区别。我们本可以把它们称为“高阶类”但没有人注意到这个,因为 Java 背后没有一个强大的学术社区。

    那么怎样,何时应该使用高阶函数呢?我很高兴你这样问。如果你不曾考虑类的层次,就可能写出了一整团堆砌的代码块。当你发现其中一些行的代码重复出现,就把他们提取成函数(幸运的是这些依然可以在学校里学到)。如果你发现在那个函数里一些逻辑动作根据情况有变,就把他提取成高阶函数。糊涂了?下面是一个来自我工作的实例:假如我的一些 Java 代码接受一条信息,用多种方式处理它然后转发到其他服务器。

    class MessageHandler {
    void handleMessage(Message msg) {
    // …
    msg.setClientCode(”ABCD_123″);
    // …

    sendMessage(msg);
    }

    // …

    }

    现在假设要更改这个系统,现在我们要把信息转发到两个服务器而不是一个。除了客户端的代码一切都像刚才一样——第二个服务器希望这是另一种格式。怎么处理这种情况?我们可以检查信息的目的地并相应修改客户端代码的格式,如下:

    class MessageHandler {
    void handleMessage(Message msg) {
    // …
    if(msg.getDestination().equals(”server1″) {
    msg.setClientCode(”ABCD_123″);
    } else {
    msg.setClientCode(”123_ABC”);
    }
    // …

    sendMessage(msg);
    }

    // …

    }

    然而这不是可扩展的方法,如果加入了更多的服务器,这个函数将线性增长,更新它会成为我的梦魇。面向对象的方法是把MessageHandler作为基类,在导出类中专业化客户代码操作:

    abstract class MessageHandler {
    void handleMessage(Message msg) {
    // …
    msg.setClientCode(getClientCode());
    // …

    sendMessage(msg);
    }

    abstract String getClientCode();

    // …

    }

    class MessageHandlerOne extends MessageHandler {
    String getClientCode() {
    return “ABCD_123″;
    }

    }

    class MessageHandlerTwo extends MessageHandler {
    String getClientCode() {
    return “123_ABCD”;
    }

    }

    现在就可以对每一个服务器实例化一个适合的类。添加服务器的操作变得容易维护了。但对于这么一个简单的修改仍然要添加大量的代码。为了支持不同的客户代码我们创建了两个新的类型!现在我们用高阶函数完成同样的功能:

    class MessageHandler {
    void handleMessage(Message msg, Function getClientCode) {
    // …
    Message msg1 = msg.setClientCode(getClientCode());
    // …

    sendMessage(msg1);
    }

    // …

    }

    String getClientCodeOne() {
    return “ABCD_123″;

    }

    String getClientCodeTwo() {
    return “123_ABCD”;

    }

    MessageHandler handler = new MessageHandler();
    handler.handleMessage(someMsg, getClientCodeOne);

    没有创建新的类型和新的class层次,只是传入合适的函数作为参数,完成了面向对象方式同样的功能,同时还有一些额外的优点。没有使自己囿于类的层次之中:可以在运行时传入函数并在任何时候以更高的粒度更少的代码修改他们。编译器高效地为我们生成了面向对象的“粘合”代码!除此之外,我们还获得了所有函数式编程的其他好处。当然函数式语言提供的抽象不只这些,高阶函数只是一个开始:

    currying

    我认识的大多数人都读过“四人帮”的那本设计模式,任何自重的程序员都会告诉你那本书是语言中立的(agnostic),模式在软件工程中是通用的,和使用的语言无关。这个说法颇为高贵,故而不幸的是,有违现实。

    函数式编程极具表达能力。在函数式语言中,语言既已达此高度,设计模式就不再是必需,最终你将设计模式彻底消除而以概念编程。适配器(Adapter)模式就是这样的一个例子。(究竟适配器和 Facade 模式区别在哪里?可能有些人需要在这里再多费些篇章)。一旦语言有了叫作 currying 的技术,这一模式就可以被消除。

    currying.

    适配器模式最有名的是被应用在 Java 的“默认”抽象单元——class 上。在函数式编程里,模式被应用到函数。模式带有一个接口并将它转换成另一个对他人有用的接口。这有一个适配器模式的例子:

    int pow(int i, int j);
    int square(int i)
    {
    return pow(i, 2);
    }

    上面的代码把一个整数幂运算接口转换成为了一个平方接口。在学术文章里,这个雕虫小技被叫作currying(得名于逻辑学家Haskell
    Curry,他曾将相关的数学理论形式化 )。因为在函数式编程中函数(反之如class)被作为参数来回传递,currying 很频繁地被用来把函数调整为更适宜的接口。因为函数的接口是他的参数,使用 currying 可以减少参数的数目(如上例所示)。

    函数式语言内建了这一技术。不用手动地创建一个包装了原函数的函数,函数式语言可以为你代劳。同样地,扩展我们的语言,让他支持这个技术:

    square = int pow(int i, 2);

    这将为我们自动创建出一个有一个参数的函数 square。他把第二个参数设置为 2 再调用函数 pow。这行代码会被编译为如下的 Java 代码:

    class square_function_t {
    int square(int i) {
    return pow(i, 2);
    }
    }

    square_function_t square = new square_function_t();

    正如你所见,通过简单地创建一个对原函数的包装,在函数式编程中,这就是 currying —— 快速简易创建包装的捷径。把精力集中在你的业务上,让编译器为你写出必要的代码!什么时候使用 currying?这很简单,任何时候你想要使用适配器模式(包装)时。

    惰性求值

    惰性(或延迟)求值这一技术可能会变得非常有趣一旦我们采纳了函数式哲学。在讨论并行时已经见过下面的代码片断:

    String s1 = somewhatLongOperation1();
    String s2 = somewhatLongOperation2();
    String s3 = concatenate(s1, s2);

    在一个命令式语言中求值顺序是确定的,因为每个函数都有可能会变更或依赖于外部状态,所以就必须有序的执行这些函数:首先是
    somewhatLongOperation1,然后 somewhatLongOperation2,最后 concatenate,在函数式语言里就不尽然了。

    前面提到只要确保没有函数修改或依赖于全局变量,somewhatLongOperation1 和 somewhatLongOperation2 可以被并行执行。但是如果我们不想同时运行这两个函数,还有必要保证有序的执行他们呢?答案是不。我们只在其他函数依赖于s1和s2时才需要执行这两个函数。我们甚至在concatenate调用之前都不必执行他们——可以把他们的求值延迟到concatenate函数内实际用到他们的位置。如果用一个带有条件分支的函数替换concatenate并且只用了两个参数中的一个,另一个参数就永远没有必要被求值。在 Haskell 语言中,不确保一切都(完全)按顺序执行,因为 Haskell 只在必要时才会对其求值。

    惰性求值优点众多,但缺点也不少。我们会在这里讨论它的优点而在下一节中解释其缺点。

    优化

    惰性求值有客观的优化潜力。惰性编译器看函数式代码就像数学家面对的代数表达式————可以注销一部分而完全不去运行它,重新调整代码段以求更高的效率,甚至重整代码以降低出错,所有确定性优化(guaranteeing optimizations)不会破坏代码。这是严格用形式原语描述程序的巨大优势————代码固守着数学定律并可以数学的方式进行推理。

    抽象控制结构

    惰性求值提供了更高一级的抽象,它使得不可能的事情得以实现。例如,考虑实现如下的控制结构:

    unless(stock.isEuropean()) {
    sendToSEC(stock);
    }

    我们希望只在祖先不是欧洲人时才执行sendToSEC。如何实现 unless?如果没有惰性求值,我们需要某种形式的宏(macro)系统,但
    Haskell 这样的语言不需要它。把他实现为一个函数即可:

    void unless(boolean condition, List code) {
    if(!condition)
    code;
    }

    注意如果条件为真代码将不被执行。我们不能在一个严格(strict)的语言中再现这种求值,因为 unless 调用之前会先对参数进行求值。

    无穷(infinite)数据结构

    惰性求值允许定义无穷数据结构,对严格语言来说实现这个要复杂的多。考虑一个 Fibonacci 数列,显然我们无法在有限的时间内计算出或在有限的内存里保存一个无穷列表。在严格语言如 Java 中,只能定义一个能返回 Fibonacci 数列中特定成员的 Fibonacci 函数,在 Haskell
    中,我们对其进一步抽象并定义一个关于 Fibonacci 数的无穷列表,因为作为一个惰性的语言,只有列表中实际被用到的部分才会被求值。这使得可以抽象出很多问题并从一个更高的层次重新审视他们。(例如,我们可以在一个无穷列表上使用表处理函数)。

    缺点

    当然从来不存在免费的午餐。惰性求值有很多的缺点,主要就在于,懒。有很多现实世界的问题需要严格求值。例如考虑下例:

    System.out.println(”Please enter your name: “);
    System.in.readLine();

    在惰性求值的语言里,不能保证第一行会在第二行之前执行!那么我们就不能进行输入输出操作,不能有意义地使用本地(native)接口(因为他们相互依赖其副作用必须被有序的调用),从而与整个世界隔离。如果引入允许特定执行顺序的原语又将失去数学地推理代码的诸多好处(为此将葬送函数式编程与其相关的所有优点)。幸运的是,并非丧失了一切,数学家为此探索并开发出了许多技巧来保证在一定函数设置下(function setting)代码以一特定的顺序执行。这样我们就赢得了两个世界。这些技术包括 continuation, monad 和 uniqueness typing
    (一致型别)。我只会在本文中解释continuation,把 monad 和 uniqueness typing 留到将来的文章中。有趣的是,除了确保函数求值顺序, continuation 在很多别的情况下也很有用。这点等一会儿就会提到。

    Continuations

    Continuations 对于程序设计的意义,就像《达芬奇密码》对人类历史的意义:即对人类最大秘密的惊人揭示。也许不是,但他在概念上的突破性至少和揭示了负数的平方根意义等同。

    我们在学习函数时,只是学到了一半的事实,因为我们基于一个错误的假定:函数只能将结果返回到它的调用函数。在这个意思上continuation 是广义的函数。函数不必要返回到其调用函数而可以返回到程序的任何地方。我们把”continuation” 作为参数传给一个函数,它指定了这个函数返回的位置。这个描述可能听起来更加复杂。看一下下面的代码:

    int i = add(5, 10);
    int j = square(i);

    函数 add 在其被调用的位置将结果 15 赋给了 i,接下来 i 的值被用来调用 square。注意所有的惰性求值编译器都不能调整这几行代码因为第二行依赖着第一行的成功求值。下面用 continuation 风格又称 CPS (Continuation Programming Style) 来重写这段代码,这里函数 add 会将结果返回到 square 而不是原来的调用函数。

    int j = add(5, 10, square);

    这个例子中 add 有了另一个参数 —— 一个 add 必须在它求值结束时用其返回值调用的函数。这里 square 是 add 的一个 continuation。这两种情况下,j 都将等于 255。

    这就是强制使惰性语言有序地求值两个表达式的第一个技巧。考虑下面这个(熟悉的)IO代码:

    System.out.println(”Please enter your name: “);
    System.in.readLine();

    这两行不相依赖所以编译器会自由的重新调整他们的执行顺序。然而,如果我们用 CPS 来重写这段代码,就会有一个依赖,编译器会因此而强制对这两行代码有序执行!

    System.out.println(”Please enter your name: “, System.in.readLine);

    这里 println 需要用自己的返回结果作为参数去调用 readLine 并将 readLine 返回值作为自己的返回值。这样就能确保这两行被有序执行而且 readLine 一定被执行(因为整个计算期望最后的结果为结果)。Java 的 println 返回 void 但如果它返回的是一个抽象值(readLine所期待的),我们就解决了这个问题!当然这样的链接函数调用很快就会使代码难以读懂,不过这个可以避免。比如我们可以给语言添加些语法甜点(syntactic sugar)就可以简单的按顺序输入表达式,然后由编译器自动为我们链接这些函数调用。这样就可以如愿地使用期望的求值顺序并保留一切函数式编程的好处(包括数学地对我们程序进行推理的能力)!如果还是有迷惑,记住函数是只有一个成员的类的实例。重写上述代码使得 println 和 readLine 成为类的实例,这样就对一切都清楚了。

    如果我在此结束本节,那将仅仅涉及到 continuation 最浅显的应用。用 CPS 重写整个程序,那里所有的函数都增加一个额外的 continuation 参数并把函数结果传给它。也可以通过简单地把函数当作 continuation 函数(总是返回到调用者的函数)的特殊实例来将程序转为 CPS 风格。这种转换很容易被自动化(事实上,许多编译器就是这么做的)。

    一旦我们将一个程序转为了CPS,那么很明显每个指令都将有些 continuation, 这是一个该指令在执行结束时会用其执行结果调用的函数,通常的程序中,这是一个它要返回的地址。从上面的例子中随便举个例子,比如 add(5, 10)。在用CPS风格写的程序里,add 的continuation很明显——这是一个 add 在其执行结束时会调用的函数。那么如果在非CPS的程序里,它是什么呢?当然我们可以把程序转为 CPS ,但有这个必要吗?

    其实没有必要。仔细看一下我们的 CPS 转换过程。如果尝试为它写一个编译器,然后经过长期的思考后,你意识到这个 CPS 的版本根本不需要栈!没有函数会以传统的意义“返回”,它只是用结果调用了另一个函数。我们无需在调用时将函数参数压栈再于调用结束时弹出栈,而只是简单的把他们保存在一大块内存中,然后使用跳转指令。不再需要原来的参数——他们不会再次被用到,因为没有函数会返回!

    所以,用 CPS 风格写成的程序没有堆栈,但每个函数却有一个额外的参数可被调用。不是 CPS 风格的程序没有可以被调用的这个参数,但却有栈。栈中存放着什么?只是参数和一个指向函数返回地址的指针。你看到光了吗?栈中只是放着 continuation 的信息! 栈中指向返回指令的指针本质上和 CPS 程序里将被调用的函数是等价的。如果你想探究 add(5,10) 的 continuation,只要简单地检查它在堆栈的执行点!

    这的确很简单。continuation 和栈上指向返回地址的指针是等价的,只是 continuation 是被显式传递,所以不必和函数被调用点是同一位置。如果还记得 continuation 就是一个函数,并且在我们的语言里,函数被编译为一个类的实例,你就会理解指向栈中返回指令的指针实际就是传递给 continuation 的参数,因为我们的函数(就像一个类的实例)只是一个指针。这意味着给定程序中任意时间和任意位置,你都可以去请求一个当前的 continuation (current continuation)(它就是当前的栈的信息)。

    好的,这样我们就知道了什么是 current continuation。他有什么意义?一旦我们得到了当前的 continuation 并将它保存在某处,我们就最终将程序当前的状态保存了下来——及时地冷冻下来。这就像操作系统将其置为休眠状态。一个 continuation 对象里保存了在我们获得它的地方重新启动程序的必要信息。操作系统在每次发生线程间的上下文切换时也是如此。唯一的区别是它保留着全部控制。请求一个continuation 对象(在Scheme里,可以调用 call-with-current-continuation 函数)后,你就会获得一个包括了当前 continuation
    的对象——堆栈(或者在CPS情况下则是下一个要调用的函数)。可以把这个对象保存在一个变量(或者是磁盘)里。当你用这 continuation “重启”程序时,就会转回到处你取得这个对象的那个状态。这就象切换回一个被挂起的线程或唤醒休眠着的操作系统,区别是用 continuation,你可以多次地重复这一过程。当操作系统被唤醒时,休眠信息就被销毁了。但如果那些信息没有被销毁,你也就可以一次次地将它唤醒到同一点,就象重返过去一样。有了 continuation 你就有了这个控制力!

    Continuation 应该在什么情况下使用呢?通常在尝试模拟一个本质上是无状态的应用时可以简化你的任务。Continuation 很适合在Web应用程序中使用。微软公司的 ASP.NET 技术极尽苦心地模拟状态以便你在开发 Web 应用时少费周折。可如果 C# 支持了continuation,ASP.NET 的复杂度就可以减半——你只需要保存一个 continuation,当用户下次发出 web 请求时重启它即可。对程序员来说,web 应用程序将不再有中断——程序只是简单的从下一行重启!利用 continuation 这一抽象解决问题真是令人难以置信的便利。考虑到越来越多的胖客户端应用程序正在向服务器端转移,将来 continuation 也会变得越来越重要。

    模式匹配

    模式匹配不是什么新的创新的特性。事实上,它和函数式编程的关系不大。把产生模式匹配归因于函数式编程的唯一的原因是函数式语言一度提供了模式匹配,然而现在的命令式语言还做不到。

    让我们用一个例子深入了解一下模式匹配。这是一个Java的Fibonacci函数:

    int fib(int n) {
    if(n == 0) return 1;
    if(n == 1) return 1;

    return fib(n - 2) + fib(n - 1);
    }

    让我们从Java衍生出的语言来支持模式匹配:

    int fib(0) {
    return 1;
    }

    int fib(1) {
    return 1;
    }

    int fib(int n) {
    return fib(n - 2) + fib(n - 1);

    }

    两者有什么区别?编译器为我们实现了分支。这有什么大不了?的确没什么。有人注意到很多函数包括了复杂的 swith 语句(尤其是在函数式程序中)所以认为这种抽象形式很好。我们把一个函数定义分离成多个,然后把模式置于参数中(有点象重载)。当这个函数被调用时,编译器使其比较参数和其运行时的定义然后选择其中正确的一个。这一般是通过选择可选的最特定的定义来完成。例如,int fib(int n) 可以以 n 等于 1 被调用,但是实际上 fib(n) 没有被调用,因为 fib(1) 更加特定。

    模式匹配通常要比我这个例子复杂,比如,高级模式匹配系统可以让我们这样做:

    int f(int n < 10) { ... }
    int f(int n) { ... }

    模式匹配什么时候适用?情况太多了!每当你有一个嵌套着 if 的复杂的数据结构,这时就可以用模式匹配以更少的代码完成得更好。一个很好的例子闪现在我脑海,这就是所有 Win32 平台都提供了的标准的 WinProc 函数(即使它通常被抽象了)。通常模式匹配系统能检测集合也可以应付简单的值。例如,当传给函数一个数组后,就可以找出所有首元素为 1 第三个元素大于 3 的所有数组。

    模式匹配还有一个好处即如果需要增加或修改条件,那么不必对付一个巨大的函数。只需增加或修改适合的定义即可。这消除了“四人帮”(GoF)书中的一大类设计模式。条件越复杂,模式匹配就越有用。一旦习惯了它,你就会担心没有了模式匹配的日子如何打发。

    Closures

    到此我们已经讨论了纯的函数式语言——实现了lambda演算又不包括与丘奇形式系统矛盾的语言——环境里的特性,可是还有很多在lambda演算框架之外的函数语言的有用特征。虽然一个公理系统的实现可以让我们象数学表达式那样思考程序但它未必是实际可行的。许多语言选择去合并一些函数式的元素而没有严格的坚持函数式的教条。很多象这样的语言(如Common Lisp)不要求变量是 final 的——可以即处对其修改。他们还不要求函数只依赖于其参数——允许函数访问外部状态。但这些语言也的确包含着函数式的特征——如高阶函数,在非纯粹的函数式语言里传递函数作为参数和限制在 lambda 演算系统中的作法有些不同,它需要一种常被称为词法(lexical)closure 的有趣特性。下面我给出几个例子。记住,这里变量不再是final的,函数可以引用其作用域外的变量:

    Function makePowerFn(int power) {
    int powerFn(int base) {
    return pow(base, power);
    }
    return powerFn;
    }

    Function square = makePowerFn(2);
    square(3); // returns 9

    函数 make-power-fn 返回了一个函数,它有一个参数,并对这个参数进行一定阶的幂运算。如果对 square(3) 求值会有什么结果?变量 power 不在 powerFn 的作用域中,因为 makePowerFn 已经返回它的栈桢而不复存在。那么square如何工作?一定是这个语言以某种方式将power的值保存了起来以便 square 使用。如果我们再新建一个函数cube,用来计算参数的立方又会怎样?运行环境必须存储两个power的拷贝,每个我们用 make-power-fn 生成的函数都用一个拷贝。保存这些值的现象就被称为 closure。 closure 不只保存宿主函数的参数。例如,closure可能会是这样:

    Function makeIncrementer() {
    int n = 0;

    int increment() {
    return ++n;
    }
    }

    Function inc1 = makeIncrementer();
    Function inc2 = makeIncrementer();

    inc1(); // returns 1;
    inc1(); // returns 2;
    inc1(); // returns 3;
    inc2(); // returns 1;
    inc2(); // returns 2;
    inc2(); // returns 3;

    运行时已保存了n,所以递增器可以访问它,而且运行时为每个递增器都保存了一个 n 的拷贝,即使这些拷贝本应在 makeIncrementer
    返回时消失。这些代码被如何编译?closure 在底层是如何工作的?很幸运,我们可以去幕后看看。

    一点常识会很有帮助,首先会注意到的是局部变量的生命期不再由简单的作用域限定而是不确定的。那么显然可以由此得出结论它们不再被保存在栈上——反之必须被保存在堆上[8]。这样一来,closure 的实现就象我们前面讨论的函数一样了,只是它还有一个指向周围变量的引用。

    class some_function_t {
    SymbolTable parentScope;

    // …

    }

    当一个 closure 引用了一个不在其作用域的变量时,它会在其祖先作用域中查找这个引用。就是这样!Closure 将函数式和面向对象的世界紧密结合。当你创建了一个包含了一些状态的类并把它传到别处时,考虑一下 closure。Closure 就是这样在取出作用域中的变量的同时创建“成员变量”,所以你不必亲自去做这些!

    下一步的计划?

    关于函数式编程,本文作了浅显地讨论。有时候一次粗浅的射猎可能会进展为重大的收获与我也受益匪浅。将来我还计划写写 category 理论,monad,函数式数据结构,函数式语言中的类型(type)体系,函数式并发,函数式数据库等等还有很多。如果我得以(在学习的过程中)写出了上述诸多主题中的一半,我的生命就会完整了。还有,Google 是我们的朋友。

    评论

    如果你有任何问题,意见或建议,请发到邮箱 coffee…@gmail.com。很高兴收到你的反馈

    ===========================

    [1] 我在2005年找工作时常常提出这个问题,当时我得到的是数量可观的一脸茫然。想像一下,这些人至少每人会得到30万美元,如果他们理解了他们可以得到的大部分工具。

    [2] 这像是个悖论。物理学家和数学家被迫确认他们还不完全清楚是否宇宙万物遵循着可以被数学描述的规则。

    [3] 我一直厌恶提供了一堆枯燥的日期,人名和地点的纪年式历史课。对我而言,历史是改变了这个世界的人的生活,是他们行为之后的个人动机,是他们得以影响亿万生灵的体制。所以这个关于历史的小节注定无法完整,只讨论了于此关系及其密切的人物与事件。

    [4] 我在学习函数式编程的时候,很不喜欢术语 lambda,因为我没有真正理解它的意义。在这个环境里,lambda 是一个函数,那个希腊字母只是方便书写的数学记法。每当你听到 lambda 时,只要在脑中把它翻译成函数即可。

    [5] 有趣的是 Java 的字符串是不可变更的,探讨这一离经叛道的设计的原因也非常有趣,不过在这里会分散我们对原目标的注意力

    [6] 大多数函数式编程语言的编译器能通过将递归尽可能转为迭代来进行优化,这被称为尾递归。

    [7] 相反未必成立,虽然有时可以证明两端代码等价,但这不是所有情况下都成立。

    [8] 这实际上不比存储在栈上慢,因为一旦引入了垃圾回收器,内存分配就成为了一个O(1)的操作。


    February 03

    这个时候就积极了

    拖了好久都没有派军队去铲冰除雪,救助灾民,拖到拖不下去才让大兵们出来.可这种时候却是万分积极,就因为子弟兵们有清理广场的宝贵经验和光荣传统?

     粵裝甲兵進駐 嚴防車站騷亂
      (星島) 02月 03日 星期日 05:30AM
      
      (綜合報道)
      
      (星島日報 報道)廣州火車站連續發生人踩人流血事件(死了一個女工)後,國家主席兼中央軍委主席胡錦濤 下令廣州軍區某裝甲旅一千五百名官兵進駐火車站,以防發生更大規模騷亂。廣東省委昨天發布《告全省人民書》,懇切期望外省民工能留在廣東過年。廣東交通廳 春運辦公室則發出強硬指示,要求東莞市通往廣州的所有公共巴士全部停開,阻止滯留人員湧入廣州。
      
        本報中國組
      
        華中華南地區昨天繼續降落暴雪,災情加劇。上午,廣州火車站四周再次聚集超過二十萬旅客,在大雨和寒風中等待返鄉的火車,當中不少人已滯留多天,飽受飢餓、疲乏不堪、如廁不便等問題,不少旅客已生病,感冒或腸胃不適,多數人的臉上都顯得焦慮不安,情緒接近崩潰。
      
        公安及武警在每條通道都築起數道人牆,實施分批放行。每次上車的廣播一開,大批旅客便不顧落雨及路面的積水,爭先恐後往前衝。由於人多擠 逼,現場幾度混亂,女子被擠掉行李的尖叫聲不斷傳開。大批公安不時衝入現場,阻止激動的人流。據警方統計,三天來已救出一千三百多名被擠倒、擠昏的旅客。
      
        140萬旅客焦急等待返鄉
      
        據鐵道部計算,目前還有一百四十多萬已購買返鄉火車票的旅客,準備在春節(本月七日)前搭乘火車返鄉,加上得知鐵路運輸恢復正常、一部分 原已放棄回家打算留粵過年的旅客捲土重來,估計未來幾天廣州火車站仍將處於春運高峰期。由於廣州火車站連日來維持秩序的任務相當艱巨,五、六千武警和警察 已不堪重負,廣東政府特向部隊求援。
      
        廣州軍區連夜召開緊急會議,研究出派兵救援的方案。前天凌晨五時,該方案由胡錦濤主席親自拍板,中央軍委批准執行,廣州軍區某集團軍裝甲 旅和某大隊的一千五百名官兵立即奉命緊急出動,於當天中午十二時全部到達火車站的指定崗位執勤,協助維持春運秩序。此外,參加本次救災的軍人和預備役人員 已達一百五十萬。
      
        發布《人民書》籲留粵過年
      
        廣東省委聯同省政府昨天發表《告全省人民書》,號召「全省人民立即行動起來,同心同德,萬眾一心,再接再厲,繼續做好抗災救災工作,努力奪取抗災救災工作的全面勝利。」
      
        針對返鄉民工,《告全省人民書》打溫情牌,稱「即將到來的春節是中華民族的傳統節日,我們充分理解大家的思鄉之情,誠懇地希望大家正視現 實困難,留在廣東、留在這片灑下了你們辛勤汗水的地方,過一個特別的春節。」不過,昨天滯留火車站的民工表示,無論如何也要回家,因為留下來也將面臨各種 困難。
    February 01

    我很钦佩这个人

    也就是说换了自己几乎肯定不能做到这样.

    韶关1月28日下午3时许,24岁的杜登勇行走在京珠高速山外山大桥段。得知回湖南老家的女朋友被困在京珠高速并得了感冒,心急如焚的他从深圳出发赶往女朋友处。在零下2摄氏度的寒夜里,孤身走了16个小时。本报见习记者张志韬摄

      雨水淋湿了衣服,衣服结成了冰,头发上结满冰霜,杜登勇依然没有停下寻找女友的脚步。昨天傍晚,他还是没找到女友,却在郴州病倒。杜登勇告诉记者,他感觉腿很痛,脚已经没有了知觉。“我一定要找到她。就是爬我也要爬到她跟前”。

       ■寻人启事

      杜登勇女友周永红,今年20岁,湖南监利县人,在深圳打工。1月23日乘长途大巴从深圳出发回监利老家。目前滞留在株州,失去联系,知情者请与本报联系。

      雨水淋湿了衣服,衣服结成了冰,头发上结满冰霜,杜登勇依然没有停下寻找女友的脚步。昨天傍晚,他终于走到了郴州,却与女友失去了联系。此时,他沿京珠高速的雪地已经徒步走了100多公里。

       杜登勇一度失去联系

      前天下午6时15分,记者把杜登勇送到梅花收费站后,他不顾记者劝阻,毅然沿着结满厚厚冰层的京珠高速公路,向湖南方向走去,寻找他被困在路上生病的女友。当晚,他的手机开始无法接通,和记者失去了联系。

      从梅花到郴州,有100多公里。昨天晚上,天空一直飘着雨。根据韶关当地天气预报显示,当地前晚最低气温达到零下4℃。这不由得让人为杜登勇的安危捏了一把汗。

       热心人士关注杜登勇

      昨天本报刊登了这名深圳打工男子雪地徒步寻女友的故事后,引起了社会关注。昨天上午,广州丽江花园的彭女士拨通本报记者电话,表示有亲人在株 洲,杜登勇到株洲后可以去找她寻求帮助。随后,又有多位读者打进本报热线,表示愿意给予杜登勇帮助。与此同时,株洲晚报和萧湘晨报记者也与本报记者取得联 系,表示愿意帮助杜登勇。

      但直到中午1时许,记者反复拨打杜登勇的电话,依然无法接通。

      “爬我也要爬到她跟前”

      昨天下午3时21分,记者终于接通杜登勇的电话。在电话那端,他告诉记者,他已经快走到郴州了。

      杜登勇说,前晚与记者分别后,他沿京珠高速公路走到坪石收费站后,就拐下京珠高速公路,沿着107国道前往株洲。“衣服都结冰了,头发像打了摩丝一样,硬邦邦的。”雨水打湿了杜登勇的衣服,但他没有停下自己的脚步。他说,“我一定要找到她。就是爬我也要爬到她跟前。”

      甚至连记者送给他的面包和饼干他也没吃。他说,“我想起她还困在路上,哪里吃得下呀。”

      早晨9时许,他到了湖南宜章县城。在这里,他碰到了两个同样被困在半路、步行回家的常德同乡。三个人开始结伴同行。

      两天吃了一顿热饭

      昨天下午6时许,杜登勇终于走到了郴州。两个同乡得知他的壮举后,请他去饭馆吃了一餐晚饭。这是他27日从深圳出发以来,吃的第一餐热食。这时他才感到,身体有些吃不消了。

      他在发给记者的短信中说:“我好冷,腿好痛,脚底好像坏了,好痛。”

      得知他的处境后,记者驱车赶往郴州。但却因为路面结冰等原因,被困在了路上。最后只好委托郴州的一个朋友帮助把他暂时安置在一家旅馆里。

      昨晚11时30分,记者与杜登勇再次通电话时,他告诉记者,他感觉腿很痛,脚上已经没有了知觉。

      今日零时40分许,记者赶到郴州,在旅舍见到躺在床上的杜登勇。虽然盖着两床棉被,他仍然感觉身上很冷,甚至连说话也感觉困难。

      与此同时,因为女朋友的手机没电了,他昨天也一直未能与她取得联系。(南方都市报 记者/林劲松 记者/张志韬 摄)