命悬一线,工程灾难预防和处理--《进入空气稀薄地带》笔记

48118c11f8cd4c539be28a99812211d4_b

构建之法邹欣老师在叶卡编辑的群里问到,国庆短假可有什么好 书推荐给同学们看。VincentChen大侠说,Into Thin Air。1996年5月11日,珠穆朗玛峰有史以来遇难人数最多的一天,暴风雪导致山难。后来枚苏发女士回忆这本书,不同生命阶段的人、人与自然、文化冲 突。但是当时吸引了我的是VincentChen大侠提到,他把这篇纪实当作是工程学灾难预防和处理的参考文献。从来不是自然环境导致灾难,所有事故皆是 人祸。我一夜读完,酣畅淋漓,25条笔记,日后慢慢反醒。工程研发和运维,正是命悬一线,该当战战兢兢如履薄冰。

page 4 非正常状态下的决策和操作是危险的;不要相信任何人

由于受缺氧的影响,哈里斯也昏头昏脑的,他非但没有帮我关上阀门,反而错误地将它开到最大,使我仅有的一点儿氧气被过快地消耗掉了。虽然在下面76米的南峰上我还有一个备用的,但要走到那儿,我得先在无氧状态下通过那整段完合暴露的地段。

page 5 责任的边界,优先完成自己的责任

当"希拉里台阶"上的人群散去时,我把自己扣挂在橙色的登山绳上,在费希尔被自己的冰镐绊倒时迅速绕过他,从悬崖边上垂降下去。

page 5 时间是关键因素

在距我垂直高度122米的地方,在洁净湛蓝的天空下,珠峰依然沐浴在灿烂的阳光中,我的那些朋友们嬉戏成一团,纪念登上这个星球的最高点。他们挥舞着旗 帜,拍着照片,用光了宝贵的分分秒秒。谁都不曾想到会有一场可怕的严峻考验正在逼近。毋庸置疑,在这漫长的一天即将结束之际,每一分钟都至关重要。

page 19 另一个角度,珠峰与飞机同高

浓黑如墨的山峰巍然耸立,傲视着周围的山脊。山峰直刺云霄,将飞机以每小时220公里的极速喷出的气流撕开一道裂缝。气流掀起的一缕冰晶向东蔓延开来,宛 如一条长长的丝巾。凝视着空中这道飞机划过的轨迹,我突然意识到,珠峰的最高点恰好与这架载着我穿越苍穹的飞机的飞行高度一样。猛然间,一个令我震惊的念 头跃入我的脑海:我要攀登的高度与空客300喷气式客机的飞机高度一样,这近乎异想天开,甚至比这还糟,一想到这儿,我不禁手冒冷汗。

page 25 信任

我渐感不安的主要原因,还是因为我从未与这样一大群陌生人共同攀登过。除了21年前阿拉斯加之行外,我以前的探险都是独自一人,或与一两个信任的朋友一起。

在登山活动中,对同伴的信任至关重要。一名登山者的行为将影响整个团队的利益。一个松动绳结、一次意外的失足、一块被摇动的岩石,或者其他不小心的行为,给肇事者及其队友都会带来严重的后果。因此,对于登山者而言,谨慎选择,不与不明底细的人合作,就不足为怪了。

但是,对于那些参加由向导带领登山的顾客来说,要对同伴信任就不容易办到了,他们必须把信任寄托在向导身上。当直升机嗡嗡地驶向卢卡拉时,我猜想,我的队友们都和我一样真心希望霍尔已经谨慎地将能力不足的顾客淘汰出局,并有办法保证我们免受彼此能力不足而带来的危害。

page 29 科学论证后的实验

克拉斯 基于对血氧饱和的估计,计算出人体承受氧气的最低值是970毫升/分钟,这个数值异乎寻常地接近珠峰1070毫升/分钟的测量值。克拉斯的研究,隐约指出 了氧气在8000米级山峰的攀登中起着巨大作用。他大胆推论:“一个在体格和精神上受过一流训练的登山者必将登上珠峰。如果自然环境不是太艰苦的话,他完 全不必携带氧气或其他探险用具。”1978年5月8日,意大利登山家梅斯纳尔和奥地利人哈伯勒证明了这一理论的正确性。

page 30 世外桃园 vs. 人类学标本

西方人为孔布失去了往日那种简单如画的生活而痛惜,反倒有些施恩者的味道。生活在这片崎岖乡村里的人们,磊多数并不想与现代社会或者人类进程割断联系,夏尔巴人最不愿意做的就是成为人类学博物馆里的标本。

page 36 自信而缺乏经验的危险;热情奉献不应换来承担责任

去年,一个由向导带领的美国探险队雇佣了一个名叫卡米利塔的夏尔巴男孩帮厨。这个十十一二岁、身体健壮且雄心勃勃的男孩请求美国人允许他以一名高山协作的 身份在高山上工作。虽然卡米没有任何的登山经验,也没有受过什么正规的训练,但是为了答谢他的热情和奉献精神,几个星期后,他的愿望得到了满足。

从海拔6700-7600米的常规路线中,要攀登一道名为"洛子壁"的陡峭而险象环生的冰坡。作为一种安全保护措施,探险队通常会在冰坡上由下而上地系一 系列的登山绳,而登山者在攀登时为了自我保护应在自己和固定绳之间系一根外挂绳。卡米,这个过分自信而又缺乏经验的年轻男孩认为在登山绳上系一根外挂绳的 做法大可不必。一天下午,当他背着担子攀登洛子壁的时候,他在石头般坚硬的冰面上失手坠到600米深的坡底。

page 45 导致事故的都是人的错

费希尔说,"意外事故的发生,我认为那都是人为错误所致一,所以这正是我要尽力避免的。我在年轻时曾有过许多次意外事故,你能找到诸多原因,但最终都是人的错。"

page 52 实战经验不可替代

当我为次日的行程整理装备时,我了解到我的队友们,受限于家庭和他们如日中天的事业,极少有人在去年进行过一至两次的攀登训练。虽然每个人看上去都体格健 壮,但环境所限使得他们只能在跑步机或者脚踏车上而非真正的山峰上进行他们大部分的体能训练。这使我感到踌躇。诚然,身体素质在登山中是个关键因素,但 是,还有一些同样关键的因素是在健身房里训练不出来的。

page 54 不同的问题,需要不同的角度和技巧

虽然我从未到过像孔布冰瀑这样令人毛骨悚然的冰瀑,但我攀登过许多其他的冰瀑。这些路段需要极高的使用冰镐和冰爪的专业技巧。孔布冰瀑显然不乏陡峭的冰面,但所有这些冰面都装上了梯子或路绳,抑或是二者皆有,因此传统的攀冰工具和技巧在这儿就显得多余。

我很快就认识到,在珠峰上,甚至连登山绳这个造就登山者的精髓之物,都不是按照一直以来的方式使用的。通常,一个登山者总是用一根45米长的登山绳将自己 与另一名或另两名同伴系在一起,这样每个登山者都要对别人的生命负责。以这种方式用绳子结组是个非常严肃而又友好亲密的举动。然而在孔布冰瀑,利己主义使 得我们独立攀登而不以任何方式在身体上受制于人。

达夫的夏尔巴人在从孔布冰瀑的底部到顶上固定了一根静力绳。我在腰间系了一根末端带有快挂的一米来长的外挂绳,绳子的那一头连接着快挂和安全绳。在这里, 安全感不是来自将自己系于另一名队友身上,而是来自用外挂绳将自己扣在固定绳上并顺着绳子在上面滑动攀登。以这种方式进行攀登,我们必须以最快的速度通过 冰瀑上最危险的地段,而无需将自己的生命交付技巧和经验都未知的队友。事实上,在整个攀登过程中,我们从来没有将自己的生命系在另一名队友手中。

我渐渐明白,安全是要靠一定的速度来保障的。我以我能达到的最快速度向较为安全的塔顶冲去……在冲塔尚未坍塌之前,我到达了它的顶上。

page 69 救援没有经验的人,会带来格外的危险

独行的挪威人、中国台湾人,尤其是南非人,经常成为霍尔指挥帐里的热门话题。"这山上有这么多不够资格的人,"4月底的一个晚上,霍尔愁眉不展地说,"我想这个登山季是不会太平的。"

(登山者似乎必须救助遇险的其他登山队,但是日本人放弃了印度登山队,也没有受到额外的谴责。)

page 113 权威

离开大本营的前一天,霍尔召集全体队员到指挥帐里介绍情况。他首先谈到了在冲顶日里服从命令听指挥的重要性:"在那里我将不会容忍任何自作主张的行为," 他直截了当地盯着我告诫道,"我的话将是绝对的法律,不容置疑。如果你们对我的某个决定有异议,我乐意回来后与你们交流,但在山上绝对不行。" (在山上,霍尔曾一度明确要求作者不得参与向导安置路绳,作者服从了,干坐着等待后面的顾客。)

page 113 明确约定

在费希尔队离开营地后不久,马卡鲁·高和三个夏尔巴人也启程了--这违背了霍尔的理解,即台湾人不在我们冲顶的同一天攀登。

(台湾人对霍尔要求不在同一天登顶的登复是,他们会选择自己任何喜欢的一天。)

page 115 团队带来的负担及等待

霍尔清楚地告诉我,我必须等全队的人都到达这块阳台装的集合地后才能继续攀登,于是我坐在背包上等。当霍尔和韦瑟斯终于出现在大家身后时,我已经在那儿坐 了90多分钟。在我等待的时候,费希尔队和台湾队都赶了上来,并从我身边超过去。由于等待的时间太长,我感到很沮丧,并且落在别人后面也让我很生气。但我 明白霍尔的基础理论,因此也只好忍住怒火。

在我34年的登山生涯中,我认识到登山运动的最大魅力就在于它强调自立、决断、应变和责任感。然而作为顾客参加登山,我发现,你被迫放弃所有这一切,甚至更多。出于安全的考虑,一位负责的向导总要循规蹈矩--他根本无法容忍让每位顾客独立做出重要决定。

部分队员的被动性就是这样在攀登的过程中被助长起来的。夏尔巴人负责探路、搭建营地、做饭和搬运所有物资。这使得我们可以养精蓄锐,大大增加了登顶的几 率。但我却感到极大的不满足。有时我觉得自己仿佛并没有真正在登山,而是由代理人包揽了一切。虽然为了能和霍尔一起登上珠峰我甘愿忍受这种角色,但我从来 没有习惯过……

(前文有提到,有些成熟的登山者选择成为顾客,是为了避免入关、行李、装备等细节,队长也认为与这样的登山者一起攀登是一种荣誉。)

page 127 角色与责任带来的问题

我们到达后,经过检查发现氧气瓶储备点里至少还有六瓶氧气是满的。然而哈里斯却不肯相信,他坚持认为这些氧气瓶都是空的,无论我和格鲁姆怎么解释也无济于事。

想知道氧气瓶里还有多少氧气的惟一办法,就是经接到流量调节阀上,然后看气压气上的显示,哈里斯大概就是用这种方法来检查南峰上的氧气瓶的。此次探险后, 贝曼德指出,如果当时哈里斯的流量调节阀被冰堵住了,那么即使氧气瓶里是满的,气压表上的显示也是零。这也就可以解释,为什么哈里斯的行为会如此古怪固 执。而如果他的流量调节阀真是出了故障,并且又没有氧气进入氧气面罩里,那么也就可以说明何以哈里斯的判断力会明显下降。

然而这种可能性,现在看来是个不争的事实,但当时我和格鲁姆都没有意识到。事后我才明白,由于缺氧,哈里斯的行为已明显不合常理,但我反映迟钝没有觉察到有丝毫的异样。

我对显而易于的事情竟熟视无睹,在某种程度上乃是由于向导-顾客的身份所致。我和哈里斯在身体素质及攀登技巧上不分伯仲。如果我们是以平等的伙伴关系在无 向导状态下一起攀登,我根本不可能忽略他的困境。但在这次探险中,他扮演的是战无不胜的向导角色,负责照顾我以及其他顾客;我们明确被灌输不能怀疑向导的 判断力。哈里斯事实上可能陷入困境,向导也可能迫切需要我的帮助,但这种想法从未在我迟钝的大脑中出现过。

(关于缺氧环境下人的判断力下降,后文提到作者与另一位登山者相遇,但是当时都认为遇到的是另一个人;台湾人向登山靴中排便。)

page 129 坚强 (勉强为之) 的代价

(韦瑟斯的眼睛做过角膜切开术,在低气压下视力下降,勉强坚持登顶,把冰晶揉进眼睛划破了角膜,失去纵深感。期待太阳升高以后视力改善,跟上其他人)

韦瑟斯说,"对自己来说是一种危险,对别人来说是一种负担"。

page 135 等级/排位,责任

生性敏感的贝德曼对他在探险队里无足轻重的位置十分清楚。"我无疑被认为是第三位的向导,"事后他坦言,"所以我尽量不多管闲事。这样做的后果就是,在我应该发表意见的时候却保持沉默,现在我为此深深自责"

……

"让顾客折返下山应该由斯科特来负责,"贝德曼解释说,"我们讨论过此事。我告诉他,作为第三位的向导,让我告诉那些付了6.5万美元的顾客下山,这会让我不舒服。于是斯科特同意这将是他的责任。但是不知何故,他并没有这样做。"

page 137 隐瞒带来危险

……布克瑞夫事后说,"但在冲顶之前他就很累了。他遇到了许多问题,耗费了太多精力。烦恼、焦虑、担心、折磨。斯科特很紧张,但他藏而不露。"

费希尔还对所有人隐瞒了一个事实:在冲顶那天他可能已经生病了。1984年,他在尼泊尔安纳布尔纳峰探险期间,感染上了一种肠道寄生虫--溶组织内阿米 巴,这种寄生虫在感染后的若干年里不可能从被感染都体内清除干净。这种虫子会不定期地从蛰伏状态突然发作,使被感染者身体出现剧烈阵痛,并且会在其肝部留 下一个囊肿。费希尔坚持认为这点小事儿没什么好担心的,因此他只向大本营的少数几个人透露过这种病。

……费希尔是路绳上最后一名登山者,他看上去异常衰弱。 (他是领队)

page 144 内疚无补于事。最佳的危机解决之道,是防止危机。

最后,跟哈奇森一样,布克瑞夫发现他所能叫醒的每一个要不是因为疾病、疲惫,要不就是因为害怕而不能给予任何帮助。

因此,这位俄罗斯向导决定自己去把这支小分队带回来。他克服了自己的极度疲惫,勇敢地冲进飓风中,在南坳搜寻了近一个小时。这是何等惊人的体力和勇气,但他没有找到任何失踪者。

然后布克瑞无并没有放弃。他返回营地,从贝德曼和舍宁那里得到更确切的方位,然后再次冲进暴风雪中。

(作者认为,布克瑞夫作为向导,此前在没有暴风雪到来时,先行下山,抛弃了顾客;布克瑞夫说,他当时急于下山为顾客烧好热茶。)

page 153 专家的决定,应该包括否定性意见

正如资深的美国向导彼得·列夫在这起珠峰山难发生之后告诉《攀岩》杂志的那样,"我们以为,人们掏钱是为了让我们做出正确的决定,其实不然,人们花钱是为了登顶。" (向导可能在顾客距离峰顶100米,能清楚看到峰顶时,要求顾客下撤。)

page 164 时间的重要性

事实上,5月10日的那场大风虽然猛烈,但也并非十分特别,只是很典型的珠峰狂风而已。如果它晚两个小时出现,可能就不会造成任何的人员伤亡。反之,如果它恰好提前一个小时,那这场暴风雪就可以轻易地夺去18名或者20名登山者的生命,其中可能包括我在内。

page 185 淘汰不合格的

减少未来伤亡事件的最简单的办法也许就是禁止使用瓶装氧气,当然紧急医疗救护除外。虽然仍有少数不计后果的人可能会在无氧登顶中丧命,但绝大多数能力不够 的登山者会在他们攀登到使自己身陷真正困境的高度之前,因体力不支而被迫返回。……当人们知道攀登珠峰没有氧气供应时,便很少有人会再去尝试。

page 187 无法完全避免危险

我很快就意识到,在这类由向导带领的登山活动中,1996年,在这山上的顾客中(我括我在内)很少有人能够真正意识到我们所面临的危险的严重性--人类在 海拔7620米以上的地带,其生存空间是极为狭小的。那些怀着珠峰构的沃尔特·米蒂 (指依靠幻想逃避平凡生活的人)们必须牢记,一旦在"死亡地带"发生意外(他们尽早都会遇到),即使是世界上最强壮的向导也可能无力拯救其顾客的生命;事 实上,1996的山难便是例证,世界上最强壮的向导有时也自身难保。我四位队友的遇难并非全是霍尔攀登系统的疏漏,事实上,他的系统可以说是最好的。但那 是珠穆朗玛峰,再好的系统在它面前都不堪一击。

page 210 善良的动机与结果不相关,愚蠢的决定

那年五月,我们在珠峰上的许多人都犯了错。正如我在本书前面指出的那样,我自己的行为就与两位队友的死不无关系。我毫不怀疑布克瑞夫在冲顶日那天的初衷是 善意的,而我也绝对确定他的用意是好的。虽然颇让我心烦的是,布克瑞夫拒绝承认他可能做出了一个相当拙劣的决定。(布克瑞夫的代笔作家指出,作者克拉考尔 身为记者的身份,可能对登山队安全不利。)

书是进入另一个世界的门径--5篇最近阅读好书的短评

1. 进入空气稀薄地带

构建之法邹欣老师在叶卡编辑的群里问到,国庆短假可有什么好书推荐给同学们看。VincentChen大侠说,Into Thin Air。1996年5月11日,珠穆朗玛峰有史以来遇难人数最多的一天,暴风雪导致山难。后来枚苏发女士回忆这本书,不同生命阶段的人、人与自然、文化冲 突。但是当时吸引了我的是VincentChen大侠提到,他把这篇纪实当作是工程学灾难预防和处理的参考文献。从来不是自然环境导致灾难,所有事故皆是 人祸。我一夜读完,酣畅淋漓,25条笔记,日后慢慢反醒。工程研发和运维,正是命悬一线,该当战战兢兢如履薄冰。

2. 罗马灭亡后的地中海世界

可视为盐野七生15本《罗马人的故事》的续集。北风卷地白草折,哥特诸民族迁入,罗马帝国衰落,地中海再无霸主;穆罕默德以弯刀团结半岛,阿拉伯人乘势而 起,伊比利亚半岛、北非、东地中海,尽是风帆。奥斯曼土耳其征战东欧,撒拉逊海盗掳掠意大利半岛,西班牙、法兰西、教皇、威尼斯、马耳他骑士团炮舰糜集。 起罗马帝国灭亡,讫美国建立拿破仑征服。中世纪的海洋,舰队里的骑士,大航海的背景,文艺复兴灿烂壁画后人们惨淡地生存。

3. 三国配角演义

很多年前读过杨叛的《小兵之死》,为大作中的小人物立传。那位被杨过大侠一掷坠城而死的小兵,让我们深思难道他们就该死么,他们是不是也有故事,而且并非 大侠的别传。管中窥豹有点像何兆武先生的《上学记》,历史阴谋主义有点像《达芬奇密码》,曲折探幽神似福尔摩斯。而绝无正经,作者声明,认真你就输了。

4. 特别版MATHEMATICA全书

这位作者,亲自写了数学软件Mathematica的核心部分;这位作者,发现了一种疑似图灵机等价的形式,悬赏并最终得到证明。公式推演,数学模型建 立,可视化。符号运算和数值计算。当公式拆解为语法树,当推演步骤拆解为等价变换、算法的图灵机等价,Mathematica就露出了它数学版的SICP 的真面目。典同学推荐我看的,他说,很多年Mathematica和这本书都没有什么变化,因为最初的版本就准确地捕捉到了本质。

5. The Martian: A Novel by Andy Weir

孤身被困火星,怎样生存才能活到救援到来?摆在面前的是一个个难题,从长期的食物和水匮乏,到短期的几小时以后氧气耗尽。作者用几章的篇幅构造一个个生死 攸关的难题,然后用几个小节利索解决。纯粹的硬科幻,人文关怀基本可以忽略,其对情节推动的必要性大致相当于儿童动画片里正义与邪恶的设定,所有问题纯靠 技术解决;绝对的硬科幻,没有怪力乱神,连阿瑟.克拉克式的神也没有,只有人定胜天;低科技的硬科幻,解决问题的技术,初中物理化学即可读懂,在持续几章 的提心吊胆以后,解决方案之妙令人直拍大腿。中文版和电影版已出。

博客会手工同步到以下地址:

[http://zhuanlan.zhihu.com/younggift]

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

东汤镇纪行

89af1646ad4fd7438a4c21c860cc6b49_b
1.发现自己心情不佳,攻击性强,大运动量也不足以补救。果断开始这次说走就走的旅行。订好火车票,大人逃班,孩子逃学。只要先活下来,哪管我身后洪水滔天。

东汤镇,我又来了。
6c5dd2fc712887f3d9ade276f1a84d30_b
每次出门,带哪几本书是最令人头疼的。我担心,万一无聊,万一等车之类。另外,平时繁忙,难得静下心来一口气读完的,正适合旅行。虽然有了kindle和 ipad以后有所好转,但是仍然并非每一本书都有电子版,也并非每一本书都适合非纸质媒质。好在这次二猫妈说,行李箱足够大,想带多少都行。带了四本,编 码、自由与组织、复杂系统理论基础、最美的自然图鉴。再准备好十几本电子版,出发喽!

一大早,天色已经大亮,我头脑还不清楚,不清楚到懒得抬手拿手机看时间。这时就已被人吵醒。一位女性重复了二十几遍同一句话,”昨天晚上那个列车 员,我问他,三点五十八到非要告诉我四点五十八到,这什么业务”。另有一位女性,兴高采列地嚷着,”我去了黑龙江漠河,你们东北真是凉快啊。我在珠海工 作,是湖南人。湖南~”微有口音,有点像”伏兰”。旁边一位男性的声音,”嗯,你这么一说,还真像湖南人。”冷场了。

车棚顶,有时是白光一片,有时映着绿色。我又沉沉睡去。

2.

上午到达宾馆,已经没有早餐供应,出去对付一口,买好海鲜,摊开行李,泡一通温泉,蒙头大睡到中午。

快要醒的时候,朦胧里,阳光刺眼。一片寂静里,听到哪位大爷的戏匣子开着,里面是单田芳还是谁的评书,哑着嗓子。偶有虫鸣。

宾馆全体职员全都和老板旅游去了。这事儿以前似乎也发生过一次,我当时还笑,这么好的地方,去哪里旅游呢。海鲜不能加工,没问题,在海鲜行煮好拎 走;缺桌子椅子,从宾馆借来;电视接上音频线和视频线,前两次来东汤的时候分别跑了几家店才买到的,这次没有忘记带来;咖啡不易分杯,买一个大号的啤酒 杯,权作容器。

3.

又一天早晨,准备去抓鱼,前一天已经看准了哪里卖渔具。住同一宾馆的老者,持一口上好普通话,约我们去他家园子走走,吃樱桃,摘草莓。东汤镇地处辽 宁丹东凤凰城,当地辽东口音,辨识度极高。后来知识这位普通话老者是哈尔滨人,宾馆老板的哥哥。他给我们讲了一通路线。这不是城市,有某某街某某路,他的 讲法,就是到第几座桥不要通过,向左走柏油路,再经过堡 (音pu3)子,向左走小路。四间白房子,门口有篮球架子。我查地图,一大片空白接一大片空白,中间有”陈家堡子”。他说,对对,就是那里。

上次跟一众人等同来的时候,往那个方向去过,周六有集。所以,我们拍着胸脯说,没问题,能找到。老人家说,那我先往那儿去了。我们说,买完渔具我们就去。

非常难找。我们几乎翻遍了堡子,问了不下五位,”四间白房子,有篮球架子,有樱桃树,有草莓”。大多回答,没这户人家,也有的想半天,说俺们也不是 本地人。草莓采摘基地我们倒是见到了,不是四间白房子,而是三四人高的铁骨铮钢架结构。我四下望去,很多白房子……不见篮球架子。樱桃树,甚至河沟边就 有,无人照料,野地自结果。

已经打算放弃了。心想,找了这么久,也对得起老人家了,顶多我们不去别人家采摘就是。突然遇到两位乡亲,其中一位回答我,”反正俺们这堡子没这户人家。”我天光一亮,”俺位这堡子”,你们是啥堡子啊。说是”关家堡子”,但是我要找的是陈家堡子。

退出当前堡子,回到柏油路,继续往深处走。又遇左转土路,不管三七二十一,走进去试试。行不数百米,我大呼,”篮球架子!”老人家正在大铁门里站着招手,就是这里啦。

亭亭小院,遮荫葡萄待熟。数垄生菜,根根挺立。还有黄瓜,分成四个阶段播种和成熟,某一批可吃,某一批十天后开园。后园有地瓜成片,说今年可产六百斤。院墙齐腰胸,可一跃而过。老人家说,此地民风甚好,十年来什么也没有丢过。

老人兴致勃勃地给二猫讲,这是啥啥,这是啥啥,每种作物,都自豪地介绍一遍。二猫指着蔓藤问,”草莓呢?”她心目中的草莓,不是一种作物,而是红红酸甜可吃的。跟铁犁合影。参观扣鸟的笼子,说是上午还抓到一只,以为我们不能来了,刚刚放掉。

樱桃满树,红红绿绿。老人家已经事先摘了一杯,透明玻璃,半透明的红樱桃。坐在石桌石凳边吃,解一路寻找的劳顿。

听说我们回程要去抓鱼,老人家摇头,河里是有鱼,你们这工具可不行。路遇渔翁渔婆,也是持普通话,也是大摇其头,你们可抓不到。二位各奇一辆自行车,内有若干渔具,术语甚多,我们深怀疑此行抓鱼要落空。

选了路边的放出的稻田水泡子,静水甚深,一片浓绿。布下陷阱,静等来鱼。二猫妈有方,我们是为了抓鱼,不是为了抓到鱼。她把扣鱼的那个东西叫做”曲 柳儿”,是个一圈全是网的长方笼子,两侧有入口,鱼从那里进去以后就迷路了。内置火腿肠少半根作为诱饵,我跟二猫在放置诱饵时亲自尝了尝,味道香。

准备空载而归,起网时却发现内有一条鱼,使劲蹦。一keda笼子,又掉下一只。哈哈,怎么会没有鱼。再布下网,中午吃完饭,下午再来,又有两条。估计已经进来多条,这么久,寻路而逃了吧。

砍了条柳树作拐杖涉水布钩,所幸没有受伤。一条也没有钓到,仅钩住破布两条。

最先诱捕到的两条鱼里,有一条性格极为刚烈,一直在跳。傍晚二猫大叫,那条鱼不见了,而水和瓶子还在。我说,赶紧找,肯定是跳出来了,随时会死。在桌下找到,从那么小的纯净水瓶口跳出来的,跳过了半截瓶高的空间。

我说,英雄。二猫说,那它不应该也受到冲下水道的待遇。

晚上,二猫提醒我兑现诺言,哪怕是对一条鱼的。我打着手电把英雄鱼葬了。此后,每次路过,二猫都指着英雄鱼的墓说,你就把它埋在这儿了。

4.

又一日。上午爬山去接取神泉,喝两口,打回来泡茶。据二猫妈说,有些来旅游的人,不喝自来水 (此地自来水很腥,一股海鲜味) ,也不买纯净水,每天来打水。二猫对爬山二字很不解,提出质疑,因为全程都是有大路可走的。

自神泉再向上,到当地人洗衣处,我们发现塘中很多小鱼,影子一样飘移,从一处柳丛飞到另一处岸草之下。决定,下午来抓鱼。

此时发现,日程安排真是满。当然,还不算很多电话。

下午扣鱼收获甚丰,但是在两小时死了四条。我们得出结论,即使用小溪里原来的水也不行,二猫妈认为是给氧问题。总之,二猫把剩下活着的鱼都倒到了宾馆前的小花园的水池中,其中原来就有四五红色大鱼,想来能够存活。

下午的最初两条鱼不是扣到的,而是钓到的。用毛钓 (虽然和此鱼相比还是甚大,如果”毛”的意思同”毛贼”的含义表示很小的话),穿一点点火腿肠。第一条钩穿过鱼嘴,我平生第一次钓到的鱼。太容易了,因为 溪水只有十厘米左右深,清可见底,我不得不坐下免得鱼看到我。我的钩上既没有浮子也没有铅坠,用眼睛就能看到鱼把线拽跑得飞快。如果眼睛她,我估计容易看 到有没有吞下钩。第二条,根本就没有咬到钩,咬着火腿肠被我甩到岸上。第三条在空中脱了钩,或者松了嘴,掉到岸边的水里,一转眼就不见了。此后,再未钩到 一条。或许是鱼们长了见识,更可能的是,能够一口吞下那么大钩的鱼,一共就只有这么几条。

午餐特意没有回宾馆,也没有吃海鲜,因为上次来的时候,蕊哥探知一处羊汤馆,每年只开六个月,而上次是11月份寒冬,没有吃到。这次正好开了。

好浓一碗羊汤,汤少杂碎多。一勺子下去,一层层全是羊杂碎,掉落许多以后,还剩半勺子。老板她主动添汤。手把羊肉也甚嫩,粘特制调好的蒜汁,味极佳。现在写起来还是很多口水。

当时啪啪拍了照片,特意建了微信群,向上次同来的同学们一顿显摆。乐极生悲,从上方掉下两只苍蝇到我的碗里,瞬间就死了。我说,这不是有敌敌畏吧, 死这么快。当时惊呆了,过了一会儿才想起来用勺子把苍蝇的尸体舀出来。老板娘说,绝不是敌敌畏,经常有苍蝇一下锅里就死了--说这话时,她正把我那碗羊汤 里的汤倒出去,看意思是准备把羊杂碎留下来再添汤给我。我连忙说,不用啦,反正我也吃不了。

不过,苍蝇馆子的羊汤和羊肉,真是鲜美而量足啊。

5.

最后一天,又爬上山去,取前一天藏在山里没带下来的渔具,网到一只小鱼。二猫把它放到了山下的小河里,转过头扁着嘴含着眼泪,说,我舍不得,但是我要是留下它,它就得死了。

我们这次还抓到一只马莲 (长尾蝴蝶,凤蝶?),也不知道品种是不是珍贵,但是想到可能所剩不多,而且这么美,按二猫的说法,她已经舍不得留下了。放飞。

拿出野草图鉴,认了三五种植物。鬼针草,一年蓬,魁蒿,豚草。把植物与彩页一起拍照留念。鬼针草是路边的小黄花,果实或种子是很多针扎在一起,一年蓬是像白色的波斯菊。都是年年常见,不知其名。

下午三点整,返回宾馆,刚好赶上约车的时间。

列车穿过一个个隧道,是千山山脉或其余支。当从隧道里出来的一瞬间,头顶和山侧的树木投下影子,不停闪烁,和灿烂的阳光一起飞扑在白色的舱壁上。舱 壁向远处黑暗里延伸而去,像深不见底的时光。很多次,我终于录下一小段。发朋友圈的时候,突然想起建一的话,如果人能生活很多次该有多好。我引用过他的这 段签名档,在故事《北方之城》的开头,当时我正在芬兰土尔库,极夜正长。

回程漫长,我们找了没有人的包厢窝在里面。放下书,我给二猫讲了汉尼拔的故事。我说,你听说过地中海吗,她说,知道。我开始讲汉尼拔,从他9岁在神 前发誓”终生不与罗马为友”,到在西班牙崛起,到穿过阿尔卑斯山 (二猫说,有一种糖就叫这个名字;在我提到既不能走海岸线也不能走海路进,二猫说,从天上飞过去么),他在山峰的高处遥指意大利平原鼓舞士兵,他在特拉西 美诺湖之战和坎南之战 (二猫说,诡计多端,他总能想出来不一样的办法),他不进攻罗马的决定;罗马人的恐惧,胆小的将军如何拖延决战,Scipio将军如何成为真正的汉尼拔的 弟子,在扎马击败老师。

二猫问,为什么Scipio将军和汉尼拔彼此敬重,却要作战。为什么他们在同一年死去。她猜,汉尼拔在第二次布匿战争最后力主投降假投降,那一瞬间我差点突然流泪,我不得不停顿一下说,是真的,真投降。人生不如意十有八九,即使汉尼拔也有力不能及之时。

50年后迦太基的陨落,连名字也被抹去,改名阿非利加省。土地被撒上盐,诅咒永不生长作物。人民被卖为奴隶。

故事结束,我说,这就是地中海两大国家相互争战,迦太基将军汉尼拔的故事。但是三百年后迦太基城又重新建立,重新繁荣,当年被征服的奴隶的后代有些 被罗马人释放为解放奴隶,有的甚至做了罗马的执政官,还有一个皇帝。二猫妈突然问,后来重新崛起的迦太基叫什么名字呢?我笑了,就叫迦太基。

夜色渐深,我拿出kindle开始读,直到长春。
6fe6602cad09118dbe025f8301320957_b
6.

此次我们每人买了一顶草帽。二猫妈和二猫的是西部牛仔风格,我的是传统渔民风格。非常遮阳,比棒球帽还好。

此地满街银杏。此季正亭亭如盖,秋天以后,满地黄绿的银杏叶。

海鲜行已开到两家,镇中心街两头各一。有温州熟食,非常咸,肉食者也可以不吃,仅海鲜。

板栗很香。像崩苞米花一样路边摊,但是每锅的份量超过两人的食量,可以与路人合买一锅。这次没吃。

地产 蓝莓、圣女果 (小柿子)。黄瓜也很好吃,最后一天没有买到,说是所有店的都被一辆车买走了。

还有地产蜂蜜。

没有咖啡,需要自备。

9f8ca8218e47ad546cf07fb8c9941db0_b

博客会手工同步到以下地址:

[http://zhuanlan.zhihu.com/younggift]

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

当推理不需要逻辑能力,也兼 学习离散数学或数字电路有什么用处

 

yinwuluo

1.智力测验,逻辑题,鸡兔同笼,一元二次方程

有一类智力测验题,会问到"甲说丙说的是假话,乙说甲在瞎说"什么的;还有类似这样的,"凯特是你的秘书,问是不是办公室里至少有一个人长发及腰"。得分高的,就被评为智商高,或者逻辑能力强。

这种题目所涵盖的智商 (如果能够有所涵盖的话)是非常狭窄的,不包括音乐、美术、语言等很多方面。所以,我认为这种题目啥也不代表。而且,我曾经吹过牛,说这种题目与逻辑推理能力无关。有人不信,今天我证明给你看。

我们先明确一个边界,什么能够算作智力。作为人类,区别于动物的最重要的特性是,我们会使用 (或制造,如刘典同学所强调的)工具。又,按萨特的说法,你是一个什么样的人,完全取决于你的外在,而与你内心所想无法--即,如果你终生表现得勇敢,即使每一次你都吓得心中不住地发抖,按你的表现,你仍是一个的勇敢的人。

就像函数,我们只关心它的输入与输出,或者更严格地说,我们只关心它的输入与输出间的对应关系。至于函数如何实现的,是用C语言,用电子线路,用机械,用人脑,我们并不关心。

就像中文屋子 (请GOOGLE)。

就像我拿个计算机或打开EXCEL,而你练了十年珠脑速算,我 (计算器是我的一部分) 的开方、求对数、三角函数的速度可能比你还要快。

就像你在小学是个非常聪明的学生,解鸡兔同笼问题简直不假思索,本能一样。到了初中,你还是会发现,把鸡兔同笼问题设为 二元一次方程,只有这一步需要一些思考 (需要设鸡数为x,兔数为y),然后就是机械解题了。机械解题这一步,毫无智商可言。

自从有了火器,开枪杀敌,就跟体力关系不大了。工具,是人类肢体和头脑的延伸。

幸运的是,连逻辑推理,至少智力题这种程度上的,也可以用工具解决。你,加上工具,所表现出的智商,就显得很高。

当然,有一点需要点严谨。"凯特是你的秘书,问是不是办公室里至少有一个人长发及腰",这样的题目,你需要意识到 凯特 这一命名,并不意味着秘书的性别,也不 (而不是 更不) 意味头发的长度。甚至连这点严谨,如果你能使用接下来我要说的数学工具的话,也不太需要,因为当你把凯特抽象 (形式化) 为x的时候,你不能"显然"地找到性别的依据,只好认定条件不足。

2. 两道智力题的数理逻辑解法 示范

光说理论和类比,估计你也不信。下面演示两道题目。

2.1 谁是罪犯

这道题目就是从网上随便找来的。

A 说  不是我
B 说是 D
C 说是 B
D 说   B在诬陷他

假设他们其中有一个人说的是真话,那么谁是真的罪犯?
[http://zuoye.baidu.com/question/299842b63d6092288532e871ab55857c.html]

答案是: A是罪犯
有位答题的同学说,"一个一个假设为真,其余为假.只有D说的情况可以符合."

这种题目,通常有个非常漫长的解题过程: 假设A如何,然后一顿推导;假设B如何,然后又一顿推导。

下面的方法,来自从数学家 布尔 开始的方法,据说罗素和他的学生维特根斯坦在《数学原理》或《数学的原理》中首先提出了数理逻辑的这种推导手段。现在,这种方法广泛地记载在《离散数学》和《数字电路》的数理逻辑一章中。这些前辈证明了,逻辑推导与数学计算是等价的。

为了清晰,我们分3步走。

第1步,我们做个表格,横行代表每个人所说的,纵列代表假设某个人说的是真话,交叉的单元格代表在 这一假设 (某人说的是真的)下横行对应的那个人说的话是真的还是假的。

1|       a=1 b=1 c=1 d=1
2| A !a   ?   ?   ?   ?
3| B d    ?   ?   ?   ?
4| C b    ?   ?   ?   ?
5| D !d   ?   ?   ?   ?

大写 代表发言的人,小写 代表假设 (或观点中) 此人是罪犯。如,a=1 代表(假设) a是罪犯,!a 代表 (A的观点),a不是罪犯。

其中
第2行,"! a"代表 "A 说 不是我";
第3行,"d  "代表 "B 说是 D";
第4行,"b  "代表 "C 说是 B";
第5行,"!d "代表 "D 说   B在诬陷他",即 对B的观点 (d)取非。

a=1,并非严格的表述,它的含义是,a是罪犯,并且其他三个人都不是罪犯,所以,严格的表述是: a=1 & b|c|d = 0。本文为节省空间,"并非罪犯"在表头中从略,但是"推导"中仍然需要这一条件。

第2步,我们填充表格中的单元格,设为1为真 (某人是罪犯,这一命题属实) ,0为假。

1|       a=1 b=1 c=1 d=1
2| A !a   0   1   1   1
3| B d    0   0   0   1
4| C b    0   1   0   0
5| D !d   1   1   1   0

这是这么填上的,我举一个例子,如第2行的 "a=1" 这一列。

当 a = 1 时, 这一行 "!a" 的值为 ! 1,即0。

再举个例子,第5行"b=1"这一行。

当 b = 1 (并且 a|c|d=0,也即a、c、d都为0) 时,这一行 "! d"值为 ! 0,即1,

这一步里,有时我用到了一些数理逻辑 (布尔代数,完全不是逻辑推导)的小技巧。比如"a|c|d=0,也即a、c、d都为0",这是析取 (或)的特性;下面的题目里,还对蕴含作了化简,为了计算的时候更方便。值得指出的是,即使完全不使用这些技巧,也并不不会令解题难度增加多少。甚至,你只要能查到这个表达式对应的真值表,完全不会布尔代数也能解出来。因为题量小,速度也并不慢。上面这道题,填充表格,一共解了16个单一变量的表达式,都是可以秒出结果的。

第3步,查数。

我们可以注意到,第1步是把智力题 (的众人说的话这一条件) 转换为布尔代数,第2步是在布尔代数的领域中求值,第3步,把智力题的另一条件 ("假设他们其中有一个人说的是真话")用上。此处推荐 波利亚 《怎样解题》,他老人家说,只要到了卡住的地方,把所有已知条件 (及问题)都再捋一遍。

"假设他们其中有一个人说的是真话",就是把上面填充完的表格,纵向查一下,哪一列 只有一个1,其余的都是0。上面我提到过了,1代表属实。只有"a=1"符合这一要求。

意思就是,"a=1"就是答案,即a是罪犯。

回顾。

你可能会说,我这方法求了16个表达式,而如何使用"聪明"的逻辑推理,可能表达式 (或推理过程) 根本不需要这么多个。这就跟传说中的故事一样,据说P&G的博士生想了一堆办法,才解决了盒子里到底有没有装上肥皂,而民企的农民工架上台电风扇就解决了。博士的优势在于,他的知识结构和办法*总能*解决问题,而农民工及其办法下一次就不一定好使了。

你也可能会说,我这办法也不是智力 (或能力)啊,而是知识,只要你会,人人都能解决。这正是科学技术与巫术的区别。凡是人类,都能学会;学会以后,都能应用;应用以后,人人都变聪明了。注:这种聪明,不是时下中国人太多的那种聪明。

2.1 再来一道题,谁偷吃的

A说:是B偷吃的,
B说:是D偷吃的,
C说;我没有偷吃,
D说:B在撒谎!

其中有一人说了真话,谁偷吃了蛋糕?
[http://tgyd2006.iteye.com/blog/748679]

求得的表格是这样的:

a=1 b=1 c=1 d=1
A b   0   1   0   0
B d   0   0   0   1
C !c  1   1   0   1
D !d  1   1   1   0

注: a=1意思是 a=1 & b|c|d=0。其余三列同。

因为只有1个人说了真话,所以查数,找到只有1个1的列,即"c=1"。

结论,C偷吃了。推论,D说了真话。

作为对比,给出某位网友的推理过程:
----引文开始

假设A说的是真话那么蛋糕就是B吃的,但是C说的是假话"我没吃"就是"我吃了"所以蛋糕是C吃的,有此看来A说的是真话不成立!
假设B说的是真话那么蛋糕是D吃的,但是D说的是假话"B在说谎"就是"B没说谎",C同样说了假话"我没吃"就是"我吃了"所以B说的是真话也不成立!
假设C说的是真话那么B说的是假话"是D吃的"就是"不是D吃的",D说的假话承认了是自己吃的,因为矛盾,所以C说的真话不成立!
假设D说的是真话A,B都说的假话,所以排除B,D吃蛋糕的嫌疑,因为C说的也是
假话,就是"我吃了"所以蛋糕就是被C给吃的,而真正说真话的人是D!

----引文结束

对比自然语言的推导,你可能已经感觉到,布尔代数的方法,是不需要智力 (和逻辑推导能力) ,甚至不需要注意力,也不需要注意力持久的。

工具,正具有这样的特性,让每一个平凡人成为更强的人。

3.知识的力量

上述用布尔代数解智力题,不需要逻辑能力;不过,这种方法确实需要你 知道这种方法 的存在。

这对很多人是个障碍: 我用现在的手段,已经能 (很好地)解决这个问题了,为什么我还要学新的技术呢?我生活在田园牧歌似的岁月 (和村庄)里,不是很浪漫么。

如果在田园中,你发现了一种植物,你沾到它,甚至靠近它,在特定的月份,就会皮肤发痒。你几乎确定这种植物以前根本不存在,你几乎断定它就是你过敏的感恩,你几乎确定它就是传闻中的入侵物种--但是,你不知道它的名字。

怎么办?

根据植物的照片,GOOGLE目前还不能告诉你那是什么植物,更不能告诉你如何消除。就像有同学在网上放出照片,养的花蔫得快死了。有网友说,你都把花养成这样了,还有脸放上来呐?楼主说,就是这样了,我才想放出来请教它叫什么名字,这样,才能查一下如何照料啊。

不知道,就是无知。无知而期待解决问题,就是把前人发明过的痛苦过的,再来一次。如果想利用前人的经验,你至少需要知道,前人把它叫做什么。

你必须先具备基础知识 (领域) ,然后才能检索。你需要知道如何把叶子、花的形状用文字描述出来,还有叶子间的关系、花之间的关系。像这样:总状花序、掌状叶裂...有位同学贴过照片,问蓝色的很漂亮的花是什么。蓝色、漂亮、花,对于判断物种都没有用,我根据叶序和花序查,是百合。另一次。我们带着一盆多肉植物去同学聚会,小杨同学的爱人老赵,看了一眼就说,"景天"。景天是一个科,很多多肉都属于这一科。这就是领域知识所带来的专业技能,他甚至连查表都不需要。他接着问,"什么是多肉?"

另外的知识的力量的故事。

包师弟极其睿智,到什么程度呢。他曾经看着函数的图像,通过研究函数的周期,起伏的程度,仅根据几组给定自变量的函数的形状写出了函数的公式:

cos(α+β)-cos(α-β)

这么复杂的式子,我当时的感觉就是,你还要不要人活了。在牛人面前,大抵是这种感觉。

我能记住这个故事的一个原因是,在此时,我表现出了"知识的力量"。知识附身,智力有加成。我把包师弟的公式化简了,化简的方法,其实我们初中都学过,就是和差化积。

-2sinαsinβ

然后我就可以说,函数的公式怎么会那么复杂呢~~

知道 和差化积,拉近了我和包师弟的智商。

另一次是包师弟在解决一个问题时,使用了非常繁复的方法,实现这个方法写了一个挺长的程序,花费了不少时间。后来某一次他详细描述这个方法的时候,我突然说,"线性外插"。关同学也说,对啊,这就是线性外插。线性外插,等价于那么长的代码和解释,而且,方法更加稳妥确定,因为有数学证明。后来,我们又发现,这种问题不应该使用线性外插,而是 拟合。

知道 线性外插、拟合,拉近了我和包师弟的智商。

所有这些知识,植物分类、和差化积、线性外插、拟合,还有可以替代逻辑推理的布尔代数 (数理逻辑,用计算代替推理),作为工具武装我们,使我们成为更强的人类,因此更加自由。

所以,怎么可以说,什么什么知识,不会不知道又如何呢。不会不知道,就是无知,因此无助,因此囚禁于牢笼之中而不得自由。钥匙,前人曾经使用过有效的,就在你的面前,只要你一伸手就可以拿到,而你,打算赤手空拳伏虎屠龙吗,打算喊叫别人帮忙带你自由,打算继续忍受?

知识,就是解放我们的那把钥匙。

cat

-------

此博客会手工同步到以下地址:

//[http://zhuanlan.zhihu.com/younggift]

[http://zhuanlan.zhihu.com/wujun]

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

抽象之艰难,一次作业分析

1. 问题的提出,一次作业

要求实现 小学四则运算系统,系统出10道题,用户输入答案,系统判定对错,系统给出对错的数量。

这是以邹欣老师的《构建之法》作为教材,某高校的软件工程课程作业。

该作业要求迭代开发,在《构建之法》群里,企业的工程师、高校教师对某次某位同学提交的作业进行了点评。

这位同学提交的是 JAVA GUI 版本的程序。控制件的选择,每道题的两个数是文本框禁用,要求用户输入答案处是文本框,判断对错是文本框禁用;选择加减乘除题目,是按钮。

该同学实现了出10道题目。问题来了,邹欣老师问,如果我要求出40道题目呢?之所以有此一问,是因为该同学硬编码了10道题目的 控件和业务逻辑,她的代码看起来是这样的。

----节选代码开始

JTextField jfirst1, jfirst2, jfirst3, jfirst4, jfirst5, jfirst6,
jfirst7, jfirst8, jfirst9, jfirst10;// 10道题的分别的第一
个数据JLabel jsymbol1, jsymbol2, jsymbol3, jsymbol4,
jsymbol5, jsymbol6,
jsymbol7, jsymbol8, jsymbol9, jsymbol10;// 10道题的分别的符号
JTextField jsecond1, jsecond2, jsecond3, jsecond4, jsecond5, jsecond6,
jsecond7, jsecond8, jsecond9, jsecond10;// 10道题的分别的第二个数据
JTextField janswer1, janswer2, janswer3, janswer4, janswer5, janswer6,
janswer7, janswer8, janswer9, janswer10;// 10道题分别的结果

// 添加每道题的符号标签
jsymbol1 = new JLabel("?");
jsymbol2 = new JLabel("?");
jsymbol3 = new JLabel("?");
jsymbol4 = new JLabel("?");
...
// 设置答案对错的标签
jjudge1 = new JLabel("?");
jjudge2 = new JLabel("?");
jjudge3 = new JLabel("?");
jjudge4 = new JLabel("?");
...

/* 加法计算 */
if (e.getSource() == AddBtn) {
flag=1;
jsymbol1.setText("+");
jsymbol2.setText("+");
jsymbol3.setText("+");
....
fun();
random();
}

// 计算第二道题
String answer2 = null;
a2 = first2 + second2;
answer2 = Double.toString(a2);
String result2 = janswer2.getText();
if (result2.equals(answer2)) {
jjudge2.setText("对");
true1 = true1 + 1;
jjright.setText(Integer.toString(true1));
} else {
jjudge2.setText("错");
jjresult2.setText(answer2);
}
// 计算第三道题
// 计算第四道题

----节选代码结束

2. bad smell

该同学的全部代码在这里,java语言GUI版本的[http://www.cnblogs.com/tujiangfeng/p/4480053.html]。

事实上,能细心地安置10个控件,复制粘贴->修改其中的不同,是件很不容易的事情。需要耐心和精确,稍有不慎就会操作错误,而查找这个错误非常困难,因为它隐藏在面目相似的群体之中。又,耐心和精确,确是一种非常可贵的品质,难以替代,不过,科学和技术还给我们另一种力量,就是让 平凡的人 拥有更强大的力量。任何人都能学会,只要掌握就能更牛,这是科学与巫术的不同。

该同学的问题,在她稍晚的作业中自己回答了,"Duplicated Cod(重复代码)" [http://www.cnblogs.com/tujiangfeng/p/4500116.html],这是重要的代码坏味道,重要到,它排在了坏味道中的第一名。

所谓重复,就是看起来 它们相似。

该同学其实曾经完美的解决过这个问题,在她更早的作业中,C语言console版本的 [http://www.cnblogs.com/tujiangfeng/p/4399804.html],她用for循环实现了5道题目,判断题目对错,只写了一次代码, (而在JAVA GUI版本中,她写了10次)。

3. 解决之道

沉默的代码(某位资深工程师,他在群里不是这个名字,所以不知道是谁)在作业的博客中批阅道,"使用For循环重构上面的代码"。

那天,在群里得有10位左右资深工程师给出了相同的意见,用了各种不同而相似的说法。相信该同学已经蒙了,"他们到底说的是什么啊。"最打击人的,不少资深工程师对自己给出的方法加了限定和修饰,"这是非常基本的方法"。

如果该同学看到我这篇博客,请一定仔细读一下我下面这一段话。"基本"一词,不是指它简单,或者暗示你在道德 (勤奋、努力之类的)上 理应掌握,而是它极其经常在工业中应用,所以,需要掌握。需要掌握的原因,是这是本领域的"基本"技术,就像战士的射击。

资深工程师们的核心思想是,用循环去除重复。

该同学的疑问有二。一,她们没学过数组控件。对此,邹欣老师建议,用控制台实现,用意大概是先掌握抽象这一方法,至于控件什么的技术,倒在其次。该同学提到,JAVA没有学过控制台的程序。二,怎么循环呢,怎么抽象呢?

对于第二个疑问,即需要A.原则,B.精晰的可操作的指令,也需要C.示范。示范在后面,下面先给出指令。

把上述代码抽象为循环,A.原则是,找出代码中 相同的部分 和 有变化的部分。把相同的部分放在循环 (或者函数)中,把不同的部分,作为循环的变量 (或函数的参数。参数又译为变数,其义正对),或者作为变量的函数 (这个函数的意思是数学上的,即可以由变量求得)。

B. 指令如下:

把那些名字相似,只有最后一个数字不同的,不单独声明为变量,而是声明为数组;不用名字区分它们,而是使用数组的下标区分它们。

凡是对这些变量操作(读或写)的,改为对数组元素操作。对某元素操作的若干次,改为在循环中执行,每次变更 (比如递增1) 数组元素的下标。

一共三种代码需要关注。(1)不变的部分,在每次循环中都一样, (2)数组下标,每次循环都变更, (3)下标或循环变量可能不同,需要找到它们间的关系。本例中没有第 (3)种情况。

C. 示范,上面的代码,按资深工程师们的指示,大概改成下面这个意思。

----节选代码开始
List<JTextField> jfirst = new ArrayList<JTextField>();  // 10道题的分别的第一个数据
List<JLabel> jsymbol = new ArrayList<JLabel>();         // 10道题的分别的符号
List<JTextField> jsecond = new ArrayList<JTextField>(); // 10道题的分别的第二个数据
List<JTextField> janswer = new ArrayList<JTextField>(); // 10道题分别的结果

// 添加每道题的符号标签
// 设置答案对错的标签
int max_length = 10;
for(int i = 0; i< max_length; ++i)
{
jsymbol.add(new JLabel("?"));
jjudge1.add(new JLabel("?"));
}

...

/* 加法计算 */
if (e.getSource() == AddBtn) {
flag=1;
for(int i = 0; i< max_length; i++)
{
jsymbol[i].setText("+");
}
fun();
random();
}

// 计算第二道题
// 计算第三道题
// 计算第四道题
for(int i = 0; i< max_length; ++i)
{
String answer[i] = null;
a[2] = first[2] + second[2];
answer[2] = Double.toString(a[2]);
String result[2] = janswer[2].getText();
....//省略的原因不是像循环的类似,而是可按上述方法修改
}
----节选代码结束

4. 不仅如此

对于教学本身的讨论,至此可以告一段落,但是更深的讨论,才刚刚开始。在经典导论教材SICP中,一共五章的书里,用了三章讲抽象,甚至标题就叫做XX抽象。可见抽象之重要。

抽象为循环、抽象为函数,以上给出的原则、指令、示范,是非常形而下的,因此更容易操作。但是,在何处情况下应该做抽象,如何抽象,是个更深刻的问题。比如,我们是否要把四则运算中的加减乘除抽象为同一个函数么,向然后向这个函数传入一个 first class 的操作符;我们是否要把供用户选择题目类型的按钮抽象为一个数组;为什么要这样做,为什么不这样做?为什么不抽象为面向对象,为什么不抽象为AOP,为什么不使用设计模式。

如果仅回答,"使用这些技术会导致代码更复杂",那么,"复杂"在这里,很大程度上只是个人的主观体验。这位同学也可以说,抽象成控件数组,还不如复制粘贴容易呢。在做项目的时候,我的学生就真的这样质疑过。当然,我们可以回答,"当代码规模更大的时候",可是,你又如何知道代码的成长方向呢?

从一次次的执行 (执行,是动态,或复制粘贴,是静态)中,发现其中不变的部分,抽象这些部分,并允许不同的部分可以在不同的情境 (第几次循环,函数的参数)下指定;还要在代码中同时包括不变和变化的部分 (比如,用循环变量和数组下标表示变化)。

这是一项异常艰难的工作。因此,教授抽象,也非常艰难,如果不是更难的话。

刘典说,他是先学会编程,后学的英语。因此,在他看来,for的第一含义就是循环,而不是"为了"。对于工程师而言,把技术内化为本能,值得称道,不过,作为教师,这就是 (如他所说)知识诅咒。因为你忘记了当初如何艰难地掌握了这一点,甚至,你并没有深刻掌握,只是每次都类比过去的成功经验,照样应用了而已。

为什么艰难?因为在编程以外的经验中,我们鲜能发现 循环。有人可能会说,日出而作、日落而怎,每个学期、每周的课表,这不是循环吗?这不是。这不是实现完整的循环所需要的,是粗糙的。在刚刚提到的这些循环中,什么是循环变量,循环的开始和终止条件是什么,有必要存在循环变量吗,是否可以用迭代器取代,用foreach呢,在循环体中,哪些是每次循环要改变的,哪些是不变的。

有人说,这简单啊,上述问题都能答上来。但是,你在日常生活中考虑上述循环的时候,会考虑这些问题吗,还是只是粗糙地应付过去?这就是日常生活中没有循环这一经验的原则。

而在掌握一项技能之初,有类似的经验非常重要。这就是为什么物理学的力学部分,男同学普遍比女同学掌握快的原因--经验。这也是为什么物理学的电学部分,好多同学一下子就蒙了的原因,不是因为看不见摸不着,而是现实中没有这样的东西。水流、水压,这些比喻并不与电学概念完全对应,更何况大家对这些流体 (静?) 力学的经验也就呵呵。

循环的经验来自于哪里,为什么循环对很多人难以掌握?

与循环高度类似的,是高中代数中的数学归纳法,是数列的通项,是求得的sigma上面的那个i。而这些,有很多人本来学得就烂。这就是循环的抽象。当然,前面学得不好,也可以利用计算机的for循环学习这种抽象。

(题外话,类似的,与实验技能相关的,实验现象说明什么,假阴性假阳性,这些对应的是初中平面几体里的充分条件必要条件。很多人没有把逻辑推理内化,或者没有自觉地使用这一工具,因此在做实验的时候会糊涂。)

为什么抽象,如何抽象,这个话题也可以延伸到面向对象的继承和多态,不赘述。我想说的是,抽象,并不是简单和自然的,不是理应掌握的技能,而是必须掌握的技能。
e63cb2af6c80c8c3321842560ade5334_b
-------

博客会手工同步到以下地址:

[http://zhuanlan.zhihu.com/younggift]

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

这样的写法 (function(){ /* do something */ })(); 的含义, JavaScript

执行匿名函数,并且匿名函数中的变量不污染全局变量空间。

代码:

  1. <html>
  2. <head>
  3. <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  4. </head>
  5. <body>
  6. <script type="text/javascript">
  7. (function(){
  8.     var inside="inside";
  9.     document.writeln("<p>函数0 无返回值,运行,未污染全局变量空间<br>");
  10.     document.writeln("I am running in a function anonymous.<br>");
  11.     document.writeln("var inside undefined :" + (typeof inside === 'undefined') + "<br>");
  12.     document.writeln("var inside values: " + inside + "<br>");
  13. })();
  14. var res1 = (function () {
  15.     var inside="inside";
  16.     document.writeln("<p>函数1 有返回值,运行,未污染全局变量空间<br>");
  17.     document.writeln("I am running in a function anonymous.<br>");
  18.     document.writeln("var inside undefined :" + (typeof inside === 'undefined') + "<br>");
  19.     document.writeln("var inside values: " + inside + "<br>");
  20.     return 111;
  21. })();
  22. var res2 = function () {
  23.     var inside="inside";
  24.     document.writeln("<p>函数2 有返回值,运行,未污染全局变量空间<br>");
  25.     document.writeln("I am running in a function anonymous.<br>");
  26.     document.writeln("var inside undefined :" + (typeof inside === 'undefined') + "<br>");
  27.     document.writeln("var inside values: " + inside + "<br>");
  28.     return 222;
  29. }();
  30. var res3 = function foo() {
  31.     var inside="inside";
  32.     document.writeln("<p>函数3 有返回值,运行,未污染全局变量空间<br>");
  33.     document.writeln("I am running in a naming function.<br>");
  34.     document.writeln("var inside undefined :" + (typeof inside === 'undefined') + "<br>");
  35.     document.writeln("var inside values: " + inside + "<br>");
  36.     return 333;
  37. }();
  38. function bar() {
  39.     var inside="inside";
  40.     document.writeln("<p>函数4 有返回值,定义未运行,未污染全局变量空间<br>");
  41.     document.writeln("I am running in a naming function.<br>");
  42.     document.writeln("var inside undefined :" + (typeof inside === 'undefined') + "<br>");
  43.     document.writeln("var inside values: " + inside + "<br>");
  44.     return 444;
  45. };
  46. var res4 = bar();  // bar运行
  47. document.writeln("<p>全局,所有匿名函数未污染全局变量空间<br>");
  48. document.writeln("var inside undefined :" + (typeof inside === 'undefined'));
  49. document.writeln("<br>");
  50. document.writeln("function foo undefined :" + (typeof foo === 'undefined'));
  51. document.writeln("<br>");
  52. document.writeln("function bar undefined :" + (typeof bar === 'undefined'));
  53. document.writeln("<br>");
  54. document.writeln("匿名函数1返回值:" + res1);
  55. document.writeln("<br>");
  56. document.writeln("匿名函数2返回值:" + res2);
  57. document.writeln("<br>");
  58. document.writeln("命名函数foo定义并调用的返回值:" + res3);
  59. document.writeln("<br>");
  60. document.writeln("命名函数bar调用的返回值:" + res4);
  61. document.writeln("<br>");
  62. </script>
  63. </body>
  64. </html>

执行结果:

  1. 函数0 无返回值,运行,未污染全局变量空间
  2. I am running in a function anonymous.
  3. var inside undefined :false
  4. var inside values: inside
  5. 函数1 有返回值,运行,未污染全局变量空间
  6. I am running in a function anonymous.
  7. var inside undefined :false
  8. var inside values: inside
  9. 函数2 有返回值,运行,未污染全局变量空间
  10. I am running in a function anonymous.
  11. var inside undefined :false
  12. var inside values: inside
  13. 函数3 有返回值,运行,未污染全局变量空间
  14. I am running in a naming function.
  15. var inside undefined :false
  16. var inside values: inside
  17. 函数4 有返回值,定义未运行,未污染全局变量空间
  18. I am running in a naming function.
  19. var inside undefined :false
  20. var inside values: inside
  21. 全局,所有匿名函数未污染全局变量空间
  22. var inside undefined :true
  23. function foo undefined :true
  24. function bar undefined :false
  25. 匿名函数1返回值:111
  26. 匿名函数2返回值:222
  27. 命名函数foo定义并调用的返回值:333
  28. 命名函数bar调用的返回值:444

JavaScript语言实现类似这样的效果: 3.grams.flour

JavaScript语言实现类似这样的效果: 3.grams.flour

参考 Javascript: The Good Parts,第33页

代码如下:
<html>
<body>

<script type="text/javascript">

// copied from page 33 of Javascript: The Good Parts
Function.prototype.method = function (name, func)
{
this.prototype[name] = func;
return this;
}

Number.method('chi', function() {
return this*0.333;
});

document.writeln( (3).chi());
document.writeln( "<br>" );
document.writeln( (3+1).chi() );
document.writeln( "<br>" );
document.writeln( 3.2.chi() );
document.writeln( "<br>" );
document.writeln( 3.0.chi() );
// document.writeln( 3.chi() );
// SyntaxError: identifier starts immediately after numeric literal

</script>

</body>
</html>

运行结果如下:

0.9990000000000001
1.332
1.0656

0.9990000000000001

努力学习,苦中作乐。

笔记:javascript通过反射和遍历,访问对象的所有属性,及 按引用/按值传递

正读 JavaScript: The Good Parts.

1. 通过反射和遍历,访问对象的所有属性。
<html>
<body>
<script type="text/javascript">
var x = {
"first-name": "Gift",
"last-name" : "Young"
};
x.nickname = 'nick';
for(name in x){
document.writeln(name+":"+x[name]+" "+typeof x[name]+"<br>");
}
</script>
</body>
</html>
上述代码的运行结果是:
first-name:Gift string
last-name:Young string
nickname:nick string

2.  " Objects are passed around by reference. They are never copied. "

下面的试验证实,String是按值复制的。
x与stooge是同一对象的reference,所以它们的属性的值一起变化;
snick和xnick的值“来自”对象的属性,但是snick和xnick的值并不随对象的属性的值的变化而变化。
<html>
<body>
<script type="text/javascript">
var stooge = {
"first-name": "Gift",
"last-name" : "Young"
};
var x = stooge;
x.nickname = 'before';
var snick = stooge.nickname;
var xnick = x.nickname;
document.writeln("nick:"+stooge.nickname+"<br>");
document.writeln("x.nickname:"+x.nickname+"<br>");
document.writeln("xnick:"+xnick+"<br>");
document.writeln("snick:"+snick+"<br>");
document.writeln("<br>");
x.nickname = 'after';
document.writeln("nick:"+stooge.nickname+"<br>");
document.writeln("x.nickname:"+x.nickname+"<br>");
document.writeln("xnick:"+xnick+"<br>");
document.writeln("snick:"+snick+"<br>");
</script>
</body>
</html>

上述代码的运行结果是:
nick:before
x.nickname:before
xnick:before
snick:before
nick:after
x.nickname:after
xnick:before
snick:before

努力学习语法,勿将高塔起浮沙。

系统边界,不可逾越

有不少同学在读我的博客的时候,都先预设一下,这一篇是技术的呢,还是生活或扯淡的呢?其实二者并无明显的界线,如果我们不能使用相同的原则处理生活和工作,那世界该复杂到如何可怕的程度啊。

昨天新闻,长春154公交车上,一女子因为睡着了没有给老人让座,被老人殴打。细节从略。在十年多前,对这样新闻的态度将是一边倒地批评女子--怎么能不尊老爱幼呢,怎么能忍心让老人站着呢。现在反面的声音开始取得了强势地位--不是老人们变坏了,而是坏人们变老了。

就跟评判合同纠纷一样,我们应该先抛开善恶 (低于道德,或超出道德) ,来看看系统边界。否则,我们就特别容易陷入一个特别中国式的误区:要么这个家伙是个圣人,要么这个家伙是个恶棍,二者必居其一;反正正常人类是不存在的。

系统边界,是先天的或约定的,系统与系统之间的隔离。当系统A与系统B交流时,系统A可以根据系统边界的契约 (或先天的)预期系统B的行为。

在软件工程中,系统边界是整个生命周期的第一步。在自然科学中,确定问题(也就是边界)是第一步。边界是铁笼,是孙悟空给唐僧画的圈儿,绝不对逾越。

有同学说,那逾越了呢,唐僧不就跨过去了么。逾越边界会出事,比如被白骨精吃掉,或者预料之外的代价--我们应该把孙悟空被咒的痛苦作为一个变量考虑在内,而不能漠不关心。逾越边界意味着一件最大的事情发生了,即我们换了一个问题,或者说,我们换了一个系统。

争论的时候,如果双方发现讨论的根本不是一个问题,或者范围不同,就应该立即停止争论,重新界定范围。是一个道理。

明白了这个道理,就很容易理解下面这些事。

1.开车的司机说,非机动车道我占一下又如何,反正现在也没有车--ZHUMAO说,实线就是墙,压线就是撞墙。他把人的法则转换为自然的铁律来理解,容易多了。

2. 楼道里堆点东西又如何--不必讨论在消防事故万一发生时的危险,那不是你自己家的地方,任何对公共资源的不可共享的侵占,哪怕只有一秒,统统都是越
界的。

3. 素食的人评论 吃肉的也可以是好人,但是不吃肉的更好。如果你能用某个标准来评论别人,也意味着你超越了每个个体间的界限,并正在以道德实加压力。这与"我岁数大,你得给我让座"没什么区别。

从评论别人素食这一点说开去,你当然有表达的权力,但是绝无要求别人按你的期待回应的权力。如果道德附加奖惩措施,就越过了道德的边界,而开始扮演法律的角色。从不吃肉的更是善人,到殴打不给让座的乘客到浸猪笼,只有一步距离。这样的例子在我们的传统文化和反映了我们传统文化的故事里,比比皆是:师父废徒弟武功,清理门户,决定婚姻,人身依附。

所以,以素食为例,你不能说我"不是更好",甚至不应该评论"不吃肉的更好",正如我也没有恶意评论"吃肉的智商更正常一些"。当我尊重你的选择的时候,也麻烦你自觉一点。你别说我不善良,我也不说你傻;你别说某些人更善良,我也不说你以外的那些更聪明。如果你胆敢越过边界一步,看我不斩断你的手指。

有同学问我,"老师,你也不信基督教,为什么会了解这么多基督教的事儿呢。"说实话,攒这些东西,就是等你们向我传教的时候,心情不好用于反击的。我都没给你们讲共产主义什么的,你给我讲什么基督或者佛或者孔子的理想。大家相安无事,仅在世俗的世界里严守边界,不好么。

严守边界。边界有些是法律或先天约定的,有些是甚至你我的契约的。有同学说,契约不能变更么?当然能。

契约变更的要求是: 按契约要求的条款赔偿。最近看来些事件和评论,有人认为在契约破裂的时候为争取自己的权益 (不是权利)而采取些手段 (比如欺骗)是自然的。这个"自然的",就跟"我也不能咋咋样"啊一样,都是非常扯淡的。

看你是个什么样的人,不取决于你如何对付朋友,恰恰取决于你如何对待陌生人,如何对待敌人。

严守边界。这就是我们应该做的,我们唯一能做的。

契约边界的底线,就是字面上的意义。契约当初如何约定的,无比喻地、无抒情地兑现。

有的同学会说,这么简单?对,就这么简单。甚至连这么简单的,很多人都做不到。这就是为什么高晓松醉酒驾车,其错在先,却很多人为他服刑叫好,说是真男人。能不折不扣地兑现你早就想毁掉的契约并不容易。

什么情况下可以越界呢?在别人同意的前提下,付出你更多的利益,这是可以的。对别人好,为什么需要别人同意呢。因为你也不知道别人是否觉得这是他想要的,你没有权利和能力替别人选择。

当你付出利益的时候,还要注意一件事,就是不能连带把不属于你的利益也付出去。当年有官员裸捐,我到现在没有想明白,明明财产是他和他的妻子的,他一个人做得了主么?颇有些人认为能做得了自己妻子的主是男人范,其实不然,这跟打老婆或者用老婆还赌分明就是一个水平线上的。不需要太多地说理,只要考虑到你的老婆 (这一角色只是她社会角色中的一个)也是与你同样的人类,就够了。

此外,契约所产生的边界,除非另有约定,不可让度。什么意思呢?前高法副院长说,有麻烦不要找法院,要先找德高望重的邻里论断一下。

法国人的观点是,国家与公民之间,再无中介。公民唯一赋予国家的暴力权力和裁决权,不可让度与"德高望重的邻里"。这就跟初中小男生送给你这位梦中情人的玫瑰花,你再觉得它适合送给你的梦中情人,也不能把这一朵送出去。你可以拒绝,却不能转让。

上面说了这么多,边界都是由契约构造出来的。我一直隐约提到除契约以外,还有来自先天的边界。那就是一个人的自由,不可舍弃,不可让度,不可侵犯。关于自由这一边界,一个非常典型的例子是欺骗,我们下回再说。

------------------------------------------------------------

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

C#实现可变数量参数,像printf那样

C语言的printf函数,参数的数量可以是 可变的。使用 va_start 和 va_end  实现。

C++中,实现参数可变,可以用 overload。如果参数数量不是很多的话,可以一个参数实现一个函数,两个参数再实现一个函数,三个参数再实现一个函数...

或者使用C的方案。

C#中,也可以用 overload。或者 params 关键字。

下述代码除了演示 params 实现变数量的参数,还演示了 这些参数的类型不必相同。

下述代码的输出是

  1. 1
  2. 2
  3. one
  4. two
  5. aa
  6. one
  7. two
  8. aa
  9. 1
  10. 2

代码如下。

  1. namespace @params
  2. {
  3.     class Program
  4.     {
  5.         public string value = "aa";
  6.         static void Main(string[] args)
  7.         {
  8.             Program p = new Program();
  9.             foo(1, 2, "one""two", p);
  10.             System.Console.WriteLine();
  11.             foo("one""two", p, 1, 2 );
  12.         }
  13.         public static void foo(params object[] list)
  14.         {
  15.             foreach(object o in list)
  16.             {
  17.                 if (o.GetType() == typeof(int))
  18.                 {
  19.                     System.Console.WriteLine(o);
  20.                 }
  21.                 else if (o.GetType() == typeof(string))
  22.                 {
  23.                     System.Console.WriteLine(o);
  24.                 }
  25.                 else if (o.GetType() == typeof(Program))
  26.                 {
  27.                     System.Console.WriteLine(((Program)o).value);
  28.                 }
  29.             }
  30.         }
  31.     }
  32. }