攻略向,流水账向

一转眼,距上次太湖环游记已过去近2年,去年因为某凯喜结良缘外加疫情因素,和圣僧改在湖南参加婚礼,取消了一次长途骑行计划,并在酒席上约好2024必要延续骑游传统。路线选择上,最初计划的大西北路线,诸如独库、兰州-嘉峪关因为基友证照等原因被排除掉。最终在备选的长白山和济州岛中选择了后者。因为住宿环境更友好,且从上海出发仅需1个小时,实在是很方便了。时间上,为了避开热门节假日,在8月中选择了一个工作周,加上前后的周末,一共8天行程。其中4天用来环岛,分别住宿在岛的东南西北,剩下4天里,2天来回交通,1天休息购物,1天游览景点。

序:关于骑行

在选择尽量贴海的路的情况下,逆时针沿外环环岛一圈约220km,累计爬升1300m左右,拆成4天,每天50km到60km,一方面强度偏向休闲骑比较友善,同时路上停留的大静(西)、西归浦(南)、城山(东)都是有面向游客的配套设施,也能满足骑行完化身游客模式的需求。选择逆时针的原因是,一路上的自行车道基本都在马路靠海一侧,逆时针方便许多。每天骑行路线如下,原则上是不过分绕路的情况下尽量靠海。不得不说,济州的环海公路真得足够靠海,且路况良好。

另外,环岛长途骑行一定注意安全。一方面,济州岛有不少丘陵的地形,有些路的坡度可以超过10%,且有些海边的路较窄,有急弯,对于还不熟悉车感的公路车,一定要小心。(本人就因为下坡太陡,未刹住闸发生了车辆追尾,受了些皮外伤)。另一方面,城区或镇里没有专门的自行车道,需要在人行道上骑;人行道骑起来会很颠,对于急性子的骑友,会直接骑在机动车道上(有些当地骑友就是这么干的)甚至闯红灯。骑行时注意防晒,济州岛的风景和天气关系很大,晴天是最好看也是最好玩(能下海)的时候,也是最晒的时候。能物理防晒最好,做不到也要给脖子、脸上(尤其是鼻子)、手臂、手背、膝盖、小腿涂好防晒霜,否则很容易晒伤脱皮。

沿途的补给还算方便,有非常密集的便利店覆盖,包括711和当地的品牌CU,GS25。可以随时补水和补充碳水,等同于国内加油站便利店的存在。对于骑行人员可以说很友善了。

车辆上,可以选择现场租车或把车寄过去。租车有多家店铺选择,我们是在biketrip上提前预约,然后去现场付款提车的,shimano圈刹105套件公路车,4天116000KRW,约合人民币626元。车况较老旧,但性能还不错。纠结寄车还是现场租车可以对比下价格。Biketrip的店铺距离济州国际机场很近,可以乘坐公交到月城村或直接步行前往,直接在地图上搜索biketrip即可找到。可能由于语言不通,双方的英语都有口音,感觉老板有点没有耐心。附近除了biketrip还有其他的租车点,也可以试试看。

biketrip车店

当地也可以租机动车,但我们没有试过,需要自己做下功课。

序:关于衣食住行

参考济州总领馆关于中国公民赴济的领事提醒韩国济州道对中国公民实施免签入境政策,但免签不等于无条件入境。在入境前,完整填写入境卡和健康申报,一般不会有什么问题。最好保证护照有效期在半年以上,白本护照也没有关系。

入境卡

手机通讯上,可以选择提前在国内买韩国流量卡,适合双卡双待手机,优势是价格相对便宜。也可以选择国外流量包,配合开启手机的国际漫游,优势是不需要换机或者换卡。我图省事选择了后者的方案,7天的流量套餐148元,可以提前在国内办理,办理完成开启国际漫游后,出现漫游流量时才会自动开启,不用担心提前开启。漫游流量套餐可以直接在运营商app里搜索“国际漫游”办理,对比在支付宝里搜索的套餐可以选择多一个国家,更加自由,且价格一样。济州当地的免费wifi非常普及,每个公交站都搭配有免费wifi,所以也可以留意下,剩一些流量。

在济州消费,最简单的方式有一张visa信用卡,虽然银联卡和微信、支付宝在大多数连锁便利店、餐馆也支持,但支持度最好的是visa信用卡,各种店没有见过不能刷的。当然也可以提前去银行换一些现金以备不时之需。我本次取了300000KRW(约合1700左右),因为路上经常刷基友的信用卡,如果不是刻意去用现金是花不完的。另外附赠一个简单感性的价格心算公式:韩币去掉3个0乘以5,再稍微加一点点就是人民币的价格

吃喝上,我和基友主要采用开盲盒的方式,碰到有眼缘的就定下来。除了一顿烤肉和一顿烤鱼之外,其他的像是豆腐汤、炸鸡、脊骨汤、石锅拌饭这些店我们吃下来一般是人均是70左右。如果想看是否对国人的口味,可以在大众点评上搜一下店名,不过在济州或者西归浦城区里才会比较好用。餐馆里一般会有英文菜单,基本的4、6级水平就能应付。

酒店上,济州分新济州和老济州,新济州位于机场南边,免税店、各种药局位于这里,购物更方便,免税店附近可以无压力中文沟通,相对老济州较小;老济州位于机场东边,东门市场、七星街位于这里,本地人更集中,更有烟火气。可以根据自己需求选择居住的区域。新老济州间距离不远,也有非常多公交相互来往,不用担心交通问题。其他如西归浦、城山等因为城区不大,直接选择住在城区里即可。酒店可以直接在国内平台上预定,如携程、飞猪等,入店时出示身份信息即可。价格有高有低,高的有住济州君悦这种2000一晚的,低的有100多的青旅民宿,根据自身需要选择即可,一般200多到300多的价位就能住到还可以的。但需要注意:

  • 酒店一般不提供洗漱用品,如牙膏牙刷,有的可以在酒店前台购买
  • 绝大多数房间套餐不含早餐,早餐额外收费较贵
  • 入住时间下午3点之后,退房时间上午11点之前
  • 酒店一般会有冰箱,1人1瓶冰水,夏天很爽
  • 一定要注意区分双人床房双床房,差一个字少一张床,血泪教训

出行上,公交本身已经足够方便,公交站很密集,在郊区也是如此,我们环岛骑行的一路上都能看到很多公交站。公交站里可以切换语言,看不同线路的到站时间。机场内或者市区的便利店里都可以办理公交卡,工本费4000KRW,一次乘车1500KRW左右,充10000KRW大概勉强支持了我2天新老济州来往和去咸德和机场的公交费。乘车时记得上车下车要刷2次卡,另外下车前记得按STOP按钮,否则到站没有人上车时会直接开走。打车可以下载KaKao T,但价格较高,所以我们没有打过车。地图导航上,可以用naver map或者百度地图也可以。

公交站

沟通上,在新济州免税店和附近,较多店铺可以使用中文沟通,其他城区可以使用英文沟通,在村镇里可能以翻译软件 + 手脚比划。这里推荐Papago或者google翻译,在不知道招牌或者菜单的意思而且又没有英文菜单时,可以识图翻译,很方便。

Day 1:上海 -> 济州

上海(UTC+8)和济州(UTC+9)差了1个时区,济州时间要晚1个小时,我们晚上7点10分从浦东机场出发,实际只在空中飞行了1个小时(甚至比去武汉还近),但到济州时已经是当地时间9点半了,在飞行到一半的时候,空姐就发好了入境卡和健康申报给大家填写。填写建议用英文,但其实也没有太多规矩,我身边用中文填写的代购小姑娘也能顺利入境。

济州机场

出机场比较匆忙,没有买公交卡,我们只能步行3公里到酒店,顺便在路上的便利店买了张公交卡。也许是在新济州的缘故,便利店里有许多国人换大额的50000韩元,以至于到我的时候已经没有零钱可以破钱,只好刷基友的信用卡。等在酒店办好入住已经是晚上11点。躺在床上,打开电视,随便换了换台,居然还有《不能说的秘密》看。真是神奇。

不能说的秘密

为了第二天赶路,还没欣赏多久杰伦的电影,就赶紧躺下。

Day 2:济州 -> 大静

一早起来,本来打算坐公交前往租车点,在发现要等30分钟后,我们果断选择步行2.7公里前往。车店比照片里看着要老旧一些,车也是一样,庆幸的是调试下来性能并无大碍,老板简单地把车和配件准备好,告诉我们回来的那天他不在,车锁好挂在门口的架子上就行。民风真是淳朴呢。

车的合影

在穿越了颠簸的城区后,出了城之后终于可以拥有独立的自行车道。在沿着一周西路(Iljuseo-ro)继续骑行6、7公里后,可以看到一个下坡去往更贴海岸的涯月海岸路(Aewolhaean-ro),这条路去往涯月邑的同时,也有10公里左右的很贴海岸的公路可以骑行。路上还偶遇了当地的骑行车队,热情地向我们打了招呼。

城区自行车道
涯月海岸路

实际上,对于靠海的路,不仅有专门的骑行道,也会有响应的蓝色路牌指引,所以注意观察的话,不太容易走错路。

蓝色路牌

在到达涯月邑后,这段滨海路线又回到一周西路(Iljuseo-ro)主路,天空虽然晴空万里、烈日当空,庆幸的是风是凉爽干燥的,能够吹散身上的燥热,这点体验比在上海好不少。沿主路骑行3公里右拐上翰林海岸路(Hallimhae-an-ro),这里又是一条滨海公路前往翰林港。

涯月邑

小路在有自行车道的同时,车流也少了很多,再加上海岸路蜿蜒曲折,随着车子前进,在缓坡间上上下下,风景一层层展开,骑起来颇有趣味。也许是日头正盛,一路上除了渔港里忙碌的工人,看不到行人,很是安静祥和。眼看时间已近饭店,我们在CU便利店稍作休整,品尝了第一顿当地韩餐——拉面。

翰林海岸路 泡面

当地基本所有的便利店都有泡面需要的开水,甚至还有煮面机用于喜欢煮面不喜泡面的顾客。对于骑行路上的补给实在是很方便。吃饱喝足,开始处理餐余垃圾,济州实施较为严格的垃圾分类,路上除了集中的垃圾回收站外,看不到路边的垃圾桶,需要在酒店、餐馆或便利店丢弃。便利店里很贴心的已经准备了各种分类的垃圾箱,甚至湿垃圾箱还提供了干湿分离的篮子,实在贴心。

煮面机

再次启程,穿过翰林港,右拐上翰林路,无需多远就能看到翰林公园对面的狭才海水浴场(협재해변)金陵海水浴场(금능해수욕장),绿地蓝天白滩,海上游客不少,但又不至于人山人海,看起来就赏心悦目。在后续的环岛路上,这样的沙滩浴场手脚并用都数不过来,不得不感慨济州岛无与伦比的海岸线条件。

狭才海水浴场

只可惜我还有目的地在身,简单拍了照便继续出发。继续回到一周西路(Iljuseo-ro)时,天阴欲雨,到达板浦里时雨渐大,我们找了个房檐躲雨,顺便小憩一会儿。旁边碰巧是板浦浦口(판포 포구),有不少浮潜和戏水的人。淅沥的小雨似乎并没有办法浇灭他们的欢乐。

板浦里

天公给面子,不出20分钟,雨歇。在头毛三岔口靠右走头新路(Dusin-ro)进入头毛里,骑过2个路口,在翰京海岸路(HangyeongHaean-ro)三岔口右拐,进入这天第3个滨海路线,沿路有许多风力发电机,还能看到在海岸清理垃圾的学生。济州的海岸以黑色的火山岩为主,很有特色。

头毛里 风力发电机

沿着翰京海岸路一路靠右骑行,可以到达高山(Gosan),在镇里的晚霞海岸路(Noeulhaean-ro)路口,或高山西二街(Gosanseo-2-gil)路口右转到海边便是第一天的最后一段滨海路线,经过水月峯(Suwolbong)后便是长达十几公里极贴海的晚霞海岸路,恰好正是下午4、5点的时间,已经开始弱下来的阳光在广阔的洋面上留下成片的金鳞。克服一路的暴晒便是享不尽的海景,幸而还有一阵阵的清爽凉风。晚霞海岸路依旧是很好的路况,机动车很少且有独立的非机动车道。

晚霞海岸路-1 晚霞海岸路-2

驶过这段路线回到一周西路(Iljuseo-ro)后,离大静(Daejeong)已经很近了。不到1公里后,靠右进入摹瑟浦(Moseulpo)和松岳山(Mt.Songaksan)方向,终于到了第1天的目的地——大静。大静算是岛西侧最大的城镇的,我们订在了海边的一个motel,价格300+RMB,结果没想到看走了眼订了个“双人床房”,想换房的时候被告知已经没有选择了。庆幸的是房间两个方向的窗户均靠海,有一个朝西的窗户和阳台恰是欣赏夕阳的绝佳地点,伴着海声不能更惬意。

大静海景-1 大静海景-2

赏完日落已是7点半,我们开始在镇里觅食。也许是城镇规模不大,很多店8点关门,难得遇到一个有国人的点也是如此。抱着开盲盒的心态逛了一圈,最终也只有bhc连锁炸鸡店还开着。兴许炸鸡是现炸的,在加我们只有2桌顾客的情况下,苦苦等了30分钟才上菜(后来吃的很多店上菜也很慢,兴许是预制菜科技还没推广的原因)。作为第一顿当地正餐,味道一般,和家附近韩国街的炸鸡差不多。还好期待也不高。

炸鸡

大静不算大,再加上吃完炸鸡已是9点,我们兜了圈买了第二天的早餐便回了酒店。

Day 3:大静 -> 西归浦

这天又是以经典韩餐——拉面开始。提前补充了无机盐和碳水,全身防晒全副武装,我们朝着松岳山进发。路线依旧是滨海的最南端海岸路(Choenamdanhaean-ro),在经过海母海水浴场(하모해수욕장)后,就算是告别大静了,也许是海滩较小,也许是时间尚早,有可能是济州海滩太多,这里没什么人。

海母海水浴场

不到10公里的样子,爬一个缓坡就能看到右手边的松岳山,这也是第二天诸多爬坡的第一个。在坡顶能俯瞰东海和远处造型独特的山房山(산방산)

松岳山 俯瞰

下坡朝着山房山前进,告别最南端海岸路,靠右进入兄弟海岸路(Hyeongjehaean-ro),又是一段无死角的绝美海景和沙滩,山房山也在逐渐靠近,在进入龙头里(Yongmeori)后,左转沿沙溪南路进入山房路(Sanbang-ro),这也是第二个难度稍大一点的爬坡。在爬坡图中可以近距离欣赏这座奇特的山,坡顶回头可以看到从松岳山一路过来的海岸。

山房山-1 山房山-2

经过一个大下坡,沿着山房路便可以来到和顺(Hwasun),考虑到东面因为地形原因没有沿海路线,我们沿和顺路(Hwasun-ro)回到主路一周西路(Iljuseo-ro),不到1公里右转进入大平甘山路(Daepyeonggamsan-ro)便可继续贴海前进。这一路又是几个艰苦的爬坡,图中还能看到一个挺有意思的博物馆。

大下坡 博物馆

大平甘山路爬坡到顶后,已经可以远远看见山下的小镇和远处的海边。接着是一个长放坡到道路尽头可以来到和礼来路(Yerae-ro)的交叉口,我们在路边的CU便利店尝试了当地人喜欢的冰杯 + 袋装饮料的组合,组合下来一杯10+RMB的样子,看来还是蜜雪冰城划算。

坡顶 冰杯

在这个路口左转会看到一个环岛,可以选择沿礼来路(Yerae-ro)直走,或者右转到离海岸更近的礼来海岸路(Yeraehaean-ro)继续欣赏海景。由于受到河东侧高尔夫俱乐部阻碍,需要沿礼来海岸路(Yeraehaean-ro)一路爬坡回到主路再继续南下。赶上下午1、2点毒辣的太阳,一路无甚风景,隐去不表。

礼来路

在回到一周西路(Iljuseo-ro)的路口靠右行驶,沿天帝渊路(Cheonjeyeon-ro)行驶一小段,右拐到中文观光路(Jungmungwangwang-ro)便来到中文旅游区,之所以叫这个名字倒不是因为当地说中文的人多,而是音译过来的,它位于西归浦市西部,有一些值得一逛的旅游景点:柱状节理带(갯깍 주상절리)天帝渊瀑布(천제연폭포)中文海水浴场(중문해수욕장)等。提到的这几个景点就在中文观光路(Jungmungwangwang-ro)沿路附近。旅游区内酒店挺多,风景宜人,很安静。我们在拐角的M记体验了一把异国的感觉,菜式不如国内。我点的上海burger就是平替加蔬菜版的麦辣鸡腿堡。有意思的是,这里的饮料杯是硬质塑料的,不能丢进垃圾桶需要归还。

麦当劳

吃饱喝足沿中文观光路(Jungmungwangwang-ro)一个大下坡来到中文川的天际二桥上,桥两边风景甚是壮观。过桥直行沿环岛靠右骑上伊鸥路(Ieodo-ro)边来到中文洞和大浦洞的交接,柱状节理带就在第一个岔路右拐的海边。

中文川

继续沿这条路直行,路两边还能看到还未成熟的成片柑橘。之后又是上上下下的爬坡。鉴于日头实在太晒,我和基友在一个路口的便利店停下补充冰杯,顺便坐在阴凉下享受peace的一刻,老板人很热心,脸上总是笑着,虽然不太会说英语,但还是费劲地教了我们怎么扔掉垃圾。在听说我们是要骑行环岛时,也是很兴奋,只可惜没有听懂他在说什么。

冰杯-1 冰杯-2

告别老板后,经过一个下坡可以到达江汀洞(GangJeong-dong)的邮轮码头附近,可能是附近还有个军事基地,路边环岛上张贴着有趣的标语。类似的标语在江汀桥上也能看到,桥下是一条入海的小河,河水看起来冰凉刺骨,正适合现在的温度,有一些当地人在其中戏水,又是羡慕当地人的一天。

标语 江汀桥

过桥后继续前行经过一个市镇可以来到和世界杯路的交叉口,左转可以看到济州世界杯足球场,但我们当天的目的地是西归浦,于是继续直行,靠右骑行告别伊鸥路(Ieodo-ro)进入天平路。此时离西归浦市就很近了,已经有种市郊的感觉,还能看到从西归浦女子高中放学的女学生,经过最后一个爬坡便可以看到市区的轮廓。下午完美阳光的滋养下,万物的饱和度达到完美,随便一张路边的照片都像是来自宫崎骏的动画电影。

西归浦-1 西归浦-2

进入市区后,最终我们在一个爬坡的末尾到达了终点。酒店在市区西侧,距离每日偶来市场(매일올레시장)不远,窗外也能看到远处的海。200+RMB的3人间属实捡了大便宜。韩国的这种市场挺像国内菜市场和南锣鼓巷的结合,几个街区的大小,是有屋顶的,路面挺干净,路两侧有卖小吃和特产的,也有真得卖菜或卖海产品的。西归浦好歹也是和济州一样的市,比大静还是更有人气。

偶来市场-1 偶来市场-2

从偶来市场出来,我们最终决定在烤肉店解决晚餐,感谢店员的社交边界感,在我们鬼鬼祟祟地用翻译软件扫描门口韩文菜单,并没来和我们搭讪。烤肉一般,不如家附近韩国街的西塔老太太。饭后,我们闲逛到七十里餐饮一条街边的子邱里文化艺术公园(Jaguri Park),视图融入到当地人的日常生活中。夜晚并不热,这里很像家乡的广场,能看到小孩玩水、年轻人直播,草坪上情侣窃窃私语,海岸边大海轻轻冲刷。

jaguri-park

兴尽而返,晒足6个小时忘擦防晒的小腿时刻提醒着我下次不要忘记给它也涂点防晒霜。

Day 4:西归浦 -> 城山

让人庆幸又让难办的是,骑行的第三天又是一个晴天。由于提前踩好了点,我们很轻松到达了前一天的子邱里文化艺术公园。白天的公园又是一番景象,散心的人们都消失了,氛围反而更安静了下来。

白天的子邱里

沿着七十里路(Chilsimni-ro)一路可以经过正房瀑布(정방폭포)小正房瀑布(소정방폭포)。过桥后右拐上甫木浦海岸路(Bomokpo-ro),路较窄,但风景极佳。

甫木浦海岸路-1 甫木浦海岸路-2

骑行不久便可路过下晓牛沼海滩(하효 쇠소깍 해변),海滩难得一见是黑沙,也是第一次见到,很有趣。接着沿牛沼路(Soesokkak-ro)回到一周东路(Iljudong-ro),路上还可以看到神奇的牛沼河谷的风景。

牛沼沙滩 牛沼河谷

骑上一周东路(Iljudong-ro)后,一路无甚风景可看,遂提速至30,在玉美里(위미리)西靠右沿太尉路穿过玉美(위미리)直行可以到达南原邑(남원읍)。在即将进入市镇的路口右拐到南泰海岸路(Namtaehaean-ro),可以沿着南原邑的海岸线继续前进。刚一右拐,熟悉的海景就显露了出来,顿时一扫疲惫。海风里还能隐约嗅到大海的独特腥味。海岸路上人不多,游客大多在南原龙岩海水游泳场(제주남원용암해수풀장)里。这个游泳场看起来并不是纯天然的,难怪没有叫做海水浴场。

南泰海岸路-1

过了游泳场就又回到了郊外,海水拍打着黝黑的火山岩,路上没了车辆行人安静了下来,只剩身边风的呼啸,远处灯塔无言注视着一切。偶尔也能在路上看到当地的骑友,速度相近,就像是多了个骑行伙伴一样。在路上还能看到海女在工作。

南泰海岸路-2 海女

没过多久南泰海岸路(Namtaehaean-ro)又汇入到一周东路(Iljudong-ro)。我们好不容易在路边找到一家CU,可惜店里没有坐的地方,只能在店外顶着太阳享用拉面韩餐,这一顿的葡萄冰杯和前一天的桃子冰杯尝起来味道没啥区别,也是比较重的香精味道。

午餐

过了便利店没多远,跟着蓝色骑行指示牌右转进入民宿海岸路(Minsokhaean-ro),路上会经过济州民俗村博物馆,这一路又是绝美的海景。最终路线在表善里(Pyoseon-ri)的表善海滩绕回到主路,表善海滩的进深实在是夸张,海滩沿着海岸线的长度并不算长,但是垂直于海岸线的进深长的夸张,以至于从马路到海边要走很远,甚至还能在沙滩上看到小河。从路上往海看,游客像蚂蚁一般渺小。

表善海滩-1 表善海滩-2

回到一周东路(Iljudong-ro)后,在前方当地骑行伙伴的带领下,又是一路30+的狂飙,最终在喜珍加油站附近右拐到当天最后一条海岸路线——环海长城路(Hwanhaejangseong-ro)。一到小路,瞬间没了机动车,海景和海风让骑行变得格外peace,酷热和炙烤似乎也可以暂时忘却。路上能看到当地人在晾晒海产品,

晒鱿鱼 peaceland

骑行不到10公里,距离新阳(Sinyang)和城山(Seongsan)方向也越来越近。

环海长城路 新阳路牌

当能看涉地可支和城山日出峰时,距离目的地就不远了,看到时间还早,踩踏板的腿的轻松了不少。涉地岬(Seopjikoji)城山邑(Seongsan-ri)分别位于两个造型独特的半岛,其中城山的半岛更为狭长,像一只脚伸出去。城山日出峰(성산일출봉)占了前脚掌,城山港占了后脚跟。两地风景极佳,是看海的好去处。其中城山港坐船还可以前往牛岛(Udo),牛岛不大,1日游足够,果冻海还有没有海带的沙滩很绝。

涉地可支-1 涉地岬路牌

在涉地岬的海湾附近拐到涉地可支路25号街(Seopijikoji-ro 25beon-gil),我们继续向城山进发。选择住在涉地岬的可以在环海长城路(Hwanhaejangseong-ro)终点处右拐一路靠右行驶进入涉地可支路(Seopjikoji-ro)。北行沿日出路(Ilchul-ro)就到了。日出路右侧也能到海边,但海滩小且风景一般,主要以看山为主。城山里很小,我们订的房间就在日出峰山下不远,虽然没有阳台,但推开后窗就能尽览山的一面。

日出峰-1 日出峰-2

城山日出峰形成于很久前的火山喷发口,上面的图片并不能体现它的全貌,实际上日出峰是一个突出的青春痘的形状。它是韩国第一批世界自然遗产。经开发后,有步道可以走到山顶,海拔约180m,从售票处出发步行需要30分钟左右。在山顶可以俯瞰城山村和海面,很是壮观。无需买票的免费路线也可以走一小段到山脚临海的北边。总体还是很值得一看的。

城山日出峰

也许是地方小,晚饭的烤鱼我们是指手画脚外加翻译软件的帮助下解决的。饭后简单在村里逛了逛。城山邑不大,不消多久就兜了一圈,附近有个被半岛包围的内海,傍晚时分,风轻云淡,开阔平静的水面仿佛能倒映伸出看客的心绪。

内海

夜色降临,趁着游客散的差不多,转头来到日出峰。俯瞰灯火点亮的城山邑又是一番风味。

夜色

Day 5:城山 -> 济州

最后的一日的路线相对简单,沿闲岛路(Hando-ro)过了内海的闸门桥,靠右进入迎日海岸路(Haemajihaean-ro)。起初的一片海条件一般,可能也是由于天色稍阴,虽然同时海滨公路,风景对比前几天可谓平平。路上经过的下岛海水浴场(하도해수욕장)细花海水浴场(세화해수욕장)人都不算太多。快到西门洞(Seomun-dong)时,几乎要下起雨来。

海水浴场

幸而天气很给面子,11点左右天气转晴,沿着迎日海岸路(Haemajihaean-ro)兜兜转转经过月汀里海水浴场(월정리 해수욕장)时,海滩上已能看到很多练习冲浪的人。远处还有许多风车点缀。

月汀里海水浴场

海岸路很长,又约骑了10来公里,路线在金陵海水浴场(김녕해수욕장)截止,一路遇到不少当地的骑行车队,金陵海水浴场不大,但沙很干净。我们在浴场边的便利店稍作休息,旁边还看到了一个全副武装的Trek老大爷,装备很专业。

金陵海水浴场

告别浴场靠右侧分别沿金陵路(Gimnyeong-ro)和一周东路(Iljudong-ro)骑行3公里就到了咸德邑(Hamdeok-ri)。继续靠右沿新北路(Sinbuk-ro)和鸟咸海岸路(Johamhaean-ro)可以近距离感受咸德海水浴场(함덕해수욕장)。也许是距离济州市区较近,这里的游客不少。

咸德-1 咸德-2

沿鸟咸海岸路(Johamhaean-ro)继续前行。我们在戀北亭(연북정)附近又被trek老大爷追上。大爷很热情地向我们打了声招呼,之后扬长而去。

大爷 海岸

之后顺着新北路(Sinbuk-ro)和一周东路(Iljudong-ro)进入三养二洞(Samyang2-dong),就真正进入了济州市区。进了城区,自行车道也随之消失,不得不骑上人行道,外加市区内坡度较大,上下坡都比较折磨,骑行体验大打折扣。一路沿东门路 - 西门路 - 龙门路就可回到租车的地方。果然如老板所说,大门紧锁,我们自觉地还了车,上了锁。在附近月城村(Wolseong Village)坐公交前往酒店,圆满结束了这220km的环岛骑行。

城区人行道 东门市场远眺

酒店在老济州市政厅附近,交通方便,人气鼎盛,晚餐的豆腐汤 + 饺子勉勉强强,啤酒倒是不错。

豆腐汤

Day 6:购物

骑行结束了,但旅行并未结束。我们没有按最初计划再去爬个汉拿山,倒不是因为时间问题(从市区坐公交前往仅需1个小时,从西归浦出发更快),而是考虑体力有限。于是第二天以购物为主。

新济州是购物天堂,步行街、免税店、药局等距离不远,很好逛,也很好买。免税店主要指乐天免税店新罗免税店,两家店不远,可以货比三家,价格因为退税的缘故很多都比国内便宜。也许是新罗附近其他购物点更多,店内人气明显更足,代购都要更多一些,大家都是大包小包。经济基础决定上层建筑,为了照顾中国消费者,不仅各种标语都以中文为主,导购们也都能做到几乎无障碍中文交流。新罗免税店附近的药局、饭馆也是一副唐人街的样子,服务员很多国人,也可以刷微信支付宝。宝健路步行街一片也有很多店可以一逛,也是可以免税的,有的达到额度可以现场免税。不能现场退税的话,需要拿着退税单在机场值机处先自助登记,通过海关后,去16号登机口对面退税。退税时,可以选择现金或者电子转账,现金可以当场拿到,电子转账则需要一定工作日。另外,有的免税店买完之后也需要机场提货,同样是在通过海关后,凭借护照和机票在20号登机口对面提取。

乐天免税店

男人的购买力人尽皆知。不过阴差阳错在来的飞机上加了一位代购社恐(社交恐怖分子)的微信,一天20来条代购广告极大丰富了我的购买清单。不禁感慨职业代购的感染力,我的消费欲望也开始蠢蠢欲动。经过精炼总结她的广告,外她提到的“好物”包括但不限于

  • Olive young里烘焙贝果片,味道比较像是带点甜味的馍片,味道不错。Olive young像是Innisfree的感觉,代购社恐推荐的其他面膜啥的我没看太懂
  • 药局里的VC含片、VC软糖、益生菌条、祛疤膏、脚气膏、口腔溃疡膏、妇科药、护肝片、冻伤膏,新罗/乐天免税店旁边的药局里都有中文导购,也可以去了现问
  • SKII面膜,每片比国内便宜10多元的样子,乐天免税店1盒9折,2盒85折,新罗办卡也有优惠
  • 各种包包
  • 各种烟酒

虽然也不知道分配,但抱着先买再说我也买了不少。再加上韩元需要在大脑里换算一道,让价格变成单纯的数字,险些失去理智。快速完成购物任务后,我们坐车回到老济州,去东门市场看了看,顺便带了些柑橘巧克力柑橘酥(济州生产柑橘),东门市场和西归浦偶来市场比较像,但规模更大些,路更窄些。东门市场附近的七星街有很多运动品牌,人不算多,想要挑一身运动装备的可以去看看,里面还有一家较大的卖Crocs(洞洞鞋)的店。

提着大包小包忙完购物任务后,我们又顶着大太阳,爬了市政厅的大上坡,差点噶过去。最后推荐下新济州的24小时土豆脊骨汤,大众点评分挺高,味道确实不错。

24小时土豆脊骨汤

Day 7:咸德

放弃爬汉拿山后,我们选择了更休闲了咸德海水浴场一日游。考虑到太阳毒辣,我们在附近的中餐馆吃完饭又睡了个午觉后才出发。这家店的海鲜饭还不错,50+RMB,但料很足。

中餐馆

等坐公交到了咸德海水浴场时,已是下午5点,日头已温柔下来,浴场是免费的,但使用遮阳伞下的座位会有人来收费。海水除了有一些海带外,还是很干净的,也没有扎脚的岩石。

咸德海水浴场-1 咸德海水浴场-2

日落时分,可以在附近海边的咖啡厅2楼,简单点个甜点,坐在露天的吧台上,享受海景和清凉的海风,别提多自在了。注意不要点奶茶,齁甜。

咖啡厅

咖啡厅西侧还有一段路伸向大海,临近日落,在路上可以拍到360度无死角的晚霞,完美贴合这一天养生的主旋律。

日落-1 日落-2

眼看日头落下,乌云从远处扑来,我们兴尽而返,在附近乘坐公交,打道回府,等回到市区天又放晴。

Day 8:济州 -> 上海

虽然这天是晚上10点的飞机,但为了避免意外,我们简单逛了逛当地超市,买了些趁手零食,便坐公交去了机场,顺便把最后一点现金也花掉。出关很顺利,可能是排队人太多,安检比国内还松一些。早知如此,完全可以趁大家都排完队再去值机和安检。由于时区的原因,到达浦东的时候,也还是10点多,神奇的感觉。和基友道别前,我们约好下次继续这种风格的骑游。

回头想想,这趟旅程还挺幸运的,没有遇到糟糕的天气,基本全程晴天,虽然晒了点受了点罪,但却打包眼福。同时人和车也没有出什么意外,作为第一趟出国行,很是庆幸了。出发前,我也没做啥功课,定的酒店以及吃的餐馆就像是开盲盒一样。结果第一天看到了日落,第二天200+租到了3人间,后面几天酒店的位置也很方便。开的餐馆盲盒,有一些甚至还挺好吃的。可能只有不带攻略和明确目标的旅游,一直去体验去经历,才能有这样结果吧。

回到家后,和老婆约定陪她再去一次济州岛,不骑车的那种。不骑车是我提出的,毕竟济州没有她最信赖的货拉拉后勤保障。

--END--

19年得知通过豆瓣得知GEB一书时,该书初版已许多年,求之不得。幸而在21年该书再次印刷,且准备再版。于21年底果断购入。读之甚欢,虽有些艰深,但远不止镇柜神书那么简单。这一点和《失控》相比显得高下立判。

引子:《逻辑的奉献》

自指、怪圈、哥德尔定理、人工智能

从巴赫的《音乐的奉献》谈起,由卡农赋格这种带有自指音乐形式转述至艾舍尔自指矛盾的画,并发现这种形式和哥德尔不完全定理中构造自指怪圈有着异曲同工的感觉。这种怪圈似乎是固有的,系统禀赋的。不论是康托集合论里的悖论还是逻辑学里的罗素悖论,都是自指在数理逻辑中“搞破坏式”的表现。在哥德尔发表他的定理之后,图灵和丘奇从不同的角度提出不完全定理在计算机领域的应用——可计算性。

三部创意曲

这里用小品的方式引出阿基里斯,乌龟两位主角,还顺带介绍了芝诺悖论和艾舍尔画的莫比乌斯环。

WU谜题

形式系统、系统内外、判定过程

这一节以一个预设的WJU形式系统开始,用字符串替代公理、定理,让读者对形式系统有一个初步的感性认识。作者给出了一个题目,以WJ字符串为系统的起始,基于以下4条推导规则,是否能得出WU。

  • 规则1:WJ后可以跟上U
  • 规则2:Wx可以替换为Wxx,其中x为任意字符串
  • 规则3:每处JJJ可以替换为U
  • 规则4:每处UU可以被直接去掉

形式系统内的变换类似上面这样的字符变换。以此聊开去,对比人和机器,人在做一段时间运算后,会发现其中的性质,通过洞察力可以理解这里面的模式,可以灵活地跳出系统的边界之外。而对于机器,除非你告诉它这么做,它会不厌其烦地,从WJ开始,一直往下机械地计算出所有的字符串组合,完全不会意识到自己在做什么。人类在做事时,不需要刻意去区分系统内外,就可以很自然做到意识到系统的存在。而这也是机器做不到的一点。

回到这个WJU形式系统,你可以用机器的方式推导(J方式),也可以用人类的形式思考(W方式),譬如发现规则1和2增大字符串长度,3和4则是缩短字符串长度。最后的U形式对应禅宗的处事态度。判定过程意味着,在WJU这样的形式系统中,可以在有限时间内给出定理的判定结果。然而两条缩短规则让某条定理(如WU)的证明变得困难甚至不能完成。

二部创意曲

用循环嵌套命题的形式引出下一周即将提到的形式系统、

数学中的意义和形式

形式系统、公理模式、同构

在上一节WU谜题的之外,用另一个pq系统来介绍形式系统。pq系统有无数个公理。但这些公理可以用一个共同的模式生成,即x-qxp-,其中x只由横杠-组成,在公理以外,有着一条简洁的推导规则,得到所有的定理:

  • 若xqypz是一条定理,那么x-qypz-也是一条定理

在这么一个形式系统下,经过观察可以发现其中的模式:加法的遗传特性。且这个系统只有简单的一条增长规则,即在定理不断枚举的同时,也在不断变长,这使得pq系统不同于WJU系统,可以通过从顶向下的回溯或从底向上的生成来判定每一条定理是否属于系统。

实际上,作者发明的pq系统是和现实世界的加法法则是同构的,q即equals,p即plus,-即1,在这个映射下,pq系统即一个描述加法的形式系统。这种同构也是对形式系统的一种“解释”。在高层次存在一个真理和定理的对应,这种对应在你选取对于p、q、-符号的解释前不容易发现。对于形式系统的解释选取也是很有讲究的,存在着能类比到现实世界的有意义的解释和无意义的解释。

形式系统本无意义,需要解读者赋予意义,这和语言中的意义就不一样。语言中的意义是主动的,围绕符号的意义可以让我们创建新的规则,譬如—-q-p-p-p,代表4=1+1+1+1;形式系统里的意义是被动的,你可以根据意义去解读字符串,但是只能在给定规则里行动,不能创造新的。对于逐字符解读可以产生合语法句子的字符串,就可以称为良构字符串

现实和形式系统是相互独立的,并不依赖于两者间的同构关系。除了简单的加法法则,几乎整个现实世界也都可以用形式系统来抽象。这里仅以数学为例,我们所熟知的算术规则也是形式系统里的定理,映射到现实世界里,仅通过有穷情况下的验证是不够的。借助一些推导规则,可以应用到所有的数上。关于数的定理,对于“两个整数的和”这种,我们还能通过计数去同构现实世界,但对于“有无穷个素数”这种,就需要借助符号的推理。这个证明过程由许多滴水不漏的步骤联系在一起,这也暗示着存在一个结构模式可以把这些陈述串起来,这个模式可以用特定风格的词汇或符号来展示。浓缩之后,看起来平滑过渡的每一句话里包含了许多“原子化”的推理过程。

无伴奏阿基里斯奏鸣曲

借字谜和艾舍尔的镶嵌画,讨论图案和衬底,引出下节的话题。

图形和衬底

印符规则、非定理、图形、衬底、递归可枚举集

在上节pq系统的基础上,让我们设想一个Px的定理,其中x代表素数长度的横杠。首先回归WJU系统和pq系统,里面生成代表定理的字符串时,用到下面这样的印符规则

  • 读入并识别有限字符集中任意一个符号
  • 写入任意一个符号
  • 把任意一些符号从一处搬到另一处
  • 删除任意一些符号
  • 检查一个符号是否和另一个相同
  • 保存并使用之前得到的定理

用和加法规则相同的思路,可以制造一个tq系统,同样地,用一个公理模式开始:对于任意x,xqxt-,其中x仅由-组成;再加上一个推理规则:对于任意x、y、z,如果xqytz,那么可以得到xyqytz-,其中x、y、z仅由-组成。随便选取几个例子,站在“系统”外面观察下模式就能看出乘法的模式了。如果看了pq系统一节,可以很容易做到。在tq系统基础上,也可以定义能够刻画合数的定理:x、y、z仅由短杠-组成,如果xqy-tz-是定理,那么Cx是定理。使用W方式就能看到,x = (y+1) * (z+1)的模式,x的短杠数目即是合数。另外,使用J方式,不考虑短杠背后的含义,可以机械式验证一个Cx是不是定理。

现在,我们考虑如何定义一个刻画素数的定理。一个最简单的想法就是给出上面的否定形式,即x仅由-组成,如果Cx不是一个定理,那么Px是一个定理。但是其中Cx是否是一个定理并不在我们的印符规则中。印符规则只能让你看到定理是否在之前得出的定理集中,并没有一个“非定理集”给你查询。到目前为止,刻画素数的定理还只是作为Cx系统的“空洞”出现,并不能看到其中的形式。

作者在这里举了艾舍尔的一些绘画例子,有些图案在构成图形的同时,其衬底也可以作为一种图形存在。当然了,大多数情况下,可识别的形状外,其负空间不可识别。类比到作者提出的印符数论(TNT),既有所有定理集合的负空间,也有定理空间的变形副本(通过否定每个定理得到)。这两者是否能等价呢?结果先预告下,不可以。

  • 非定理集中存在真理
  • 否定每个定理得到的集合之外存在假理

当然,在素数这个具体case下,是可以用一个“正空间”等价到“负空间”上的。不过更普遍的情况下,存在一个形式系统,其负空间(非定理集)不是任何一个形式系统的正空间(定理集)。换句话说,存在非递归的递归可枚举集。其中递归即指艾舍尔画中那种相呼应特性,递归可枚举即可以发现其中的模式。其实以自然数为例,我们以固定模式抠出其中的元素,剩余元素构成的空洞就像一幅画的负空间,大概率很难找到一种显而易见的模式来定义。

回过头来说素数,实际上是可以找到这么样的定理的。首先我们正面定义出刻画不可整除的定理。给出起始的公理模式,x、y仅由-组成,xyBZCx为公理;再给出推理规则,如果xBZCy是定理,那么xBZCxy也是定理。其中BZC就代表不整除的性质。使用W方式就能理解了。在BZC的基础上,定义素数,素数不能被1和自身以外的数整除,所以从2开始,公理模式:如果–BZCz,那么zMY–是个定理;再给出推理规则:如果zMYx和x-BZCz都是定理,那么zMYx-也是定理。这两步已经构造出了无因子性。再补充一个规则,如果z-MYz是定理,那么Pz-是个定理;同时设置一个公理P–。之后的素数就可以一个个向后推广。素数的情况还比较简单,形式系统的复杂性,有向前向后推导的各种情况,导致了哥德尔定理、图灵的停机问题出现。

对位藏头诗

用藏头的对话致敬了巴赫将BACH嵌入《赋格的艺术》的尾巴。同时阿基里斯和乌龟关于所有唱机总有播放不了的唱片的讨论,同构暗示哥德尔不完全定理。

一致性、完全性和几何学

完全性、一致性、非欧几何、未定义项

在第三章的末尾,作者从龟的角色给出了唱片和唱机的小品。在这个小品上,对于任何一种唱机都有自己播放不了的唱片,播放会导致自己的摧毁。这个故事用同构的方式给哥德尔不完备定理一个感性的秒描绘。哥德尔的定理说明不存在这么一个形式系统:任何一个真陈述都可以作为系统中的定理。这段陈述涉及对一致性完全性的描述。为了解释这两种属性,我们还是从pq系统来看,我们为最初的pq系统增加一种公理模式:

  • xqxp-,其中x由-组成

之前的加法解释在现在的pq系统下出现了错位的现象,–q-p-和-q-p-都是定理,在加法解释下,系统有不一致性。我们可以调整一下解释,把q解释为小于等于,挽回之前的同构,使系统重新具有一致性。

几何学里也有类似的情形。在欧几里得的《几何原理》中,从最基本的概念和定义开始,构建了庞大的严格的欧几里得几何体系。不过欧几里得对其中最基本的“点”,“线”,“圆”下的定义,没有办法和现实世界里的“点”,“线”,“圆”区分出来。因此一些下意识就潜入了他的证明。当然这只是经典著作里的些微瑕疵。欧几里得在公设上的缺乏严格也导致后续非欧几何的出现。《几何原理》的前4个公设简洁和优美:

  • 直线段可以连接两个点
  • 直线上的任意直线段可以任意眼神
  • 任意直线段可以以一个端点为圆心,自己为半径做一个圆
  • 一切直角彼此相等

而第5条公设相比之下就显得格格不入的“丑陋”:

  • 两条直线和第三条直线相交时,在第三条直线的一侧,三条直线所夹成的内角和如果小于两个直角的和,那么在这一侧,那两条直线延长足够远后一定会相交

用更好懂的说法,即给定一条直线,和直线外一点,过该点有且只有一条直线和给定直线平行。欧几里得本人在几何证明中也尽量避免使用第5条公设。他之后的许多数学家尝试用前4条公设去推理第5条公设失败后,转换思路去否定第5条公设,之后试图寻找系统内的矛盾。最终导致非欧几何的诞生,实际上,当你摆脱现实世界中“直线”的先入为主的理解后,就能获得非欧几何的新视角。例如,在椭圆几何中,过直线外一点,没有直线与该线平行。椭圆几何中的点和线,分别可以理解为一个球上的一对对径点(球内直径的两个点)和球上的一个大圆。像这样,赋予“点”,“线”新的解释,我们就朝着几何的形式化又进了一步。这种抽离日常意义的词,就叫做未定义项。他们的意义是我们另外灌注进去的。

几何的完全形式化,需要我们把每个词都抽离成上面那样的未定义项。形式系统中这些符号的被动意义,需要人们观察者寻找一套可以同构联系符号的概念。符号可能有很多有意义的解释,例如pq系统中,将p解释成“等于”,q解释成“减”。回头来看形式系统(在某个解释下)的一致性,这里可以给一个严格的定义:

  • 其中每个定理经过解释后,都是一个真陈述

一致性也分内部一致性,和外部一致性。前者是说所有定理经过解释后,是彼此相容没有相互矛盾;后者是说每个定理解释后,是真的,那么系统加上解释是与外部世界一致的。当然这个“外部世界”不一定是我们生活的这个世界,可以是任何想象的世界,在其中的文学规则、生物规则、物理规则、数学规则甚至逻辑规则都可以和现实世界不一样。通常我们会尽量保留数学规则、逻辑规则和现实一样。

回头看非欧几何的启示,我们在构造几何学的时候,有些词具有不变的意义,作为骨骼;另一些则可以调整,即未定义项,作为血肉。调整未定义项添加新的规则公理,就可以在固定符号意义的形式系统A的基础上,构造一个形式系统B、C、D……。我们获得新知识、新词汇、感知不熟悉的事物时,也是以类似这种分层的形式进行的。在看到艾舍尔那些视错觉的画时,这种分层的认识遇到了困境,局部的认识没法凑成一个一致性的完整解释。但其实,这时还有一种办法,就是放弃每层对符号的解释,让画面里的每一处线条都是无意义的符号。这种彻底的逃避途径,有点像禅宗式的U方式

那么数论是不是也和欧几里得几何一样,对未定义项给出不同解释,就可以得到不同分支。实际上,我们还是需要保留一些最基本的不可变的部分,和逻辑一起构成“核心数论”(类似前四条公设组成的“核心几何学”),这也被称为皮亚诺算术。在这个基础上,数论也是分叉的理论,有标准和非标准的,且有无穷多个。当然在实际应用中,无需感知它们。

如果一致性是符号获得被动意义的最低条件,那么完全性就是最高条件。一致性在说“系统中的每个东西都是真的”,完全性则是倒过来“所有由系统中的符号构成的真陈述(良构字符串)都可以由系统产生”。最初的pq系统在解释下,是一个只有两数加法的想象世界,具备完全性和一致性。增加了一个公设解释后,我们给出的小于等于解释就是一致但不完全的,尽管定理产生的都是真陈述,但-q–p—这种真陈述,就没有办法在系统里生产出来。这里我们有2种解决办法,往系统增加规则让系统更“强”或紧缩解释。这里我们选择后者——把小于等于解释中的q改成“等于或加1后等于”,此时系统重新恢复完全性。可以看到,解释会改变系统的完全性和一致性

在形式化数论中,也会面临不完全性,在那里我们会被迫采用增加规则的手段来让系统完全。哥德尔不完全定理就在说任何强有力的系统,由于能力较强,反而是不完全的。

和声小迷宫

小品中用推入露、弹出锅酥、煮调饮等印象模拟程序中的入栈、出栈。同时糅合了艾舍尔的凸和凹作品,引出鬼神、元鬼神、元元鬼神关于无限递归的场景。在鬼神部分的故事中,有两点最是妙趣至极:

  • 对于造物神的递归定义(造物神——物色的——神怪)
  • 向鬼神给出的悖论愿望(我这个愿望不被实现)导致系统停机

递归结构和递归过程

递归、RTN、人工智能、不可预知性、侯世达定律

递归是计算机领域里很常用的一个概念,每次递归进栈(push)后,新的工作是低一个层次的更简单的原工作,在完成新工作后出栈(pop)回到原来的上下文。堆栈在这个过程中会记录打断当前工作的位置还有所处的环境。在音乐中也有相同的应用,每一次的变调都相当于把一个新的调式推入堆栈,在回到主调时,会有种“如释重负”的感觉(栈空)。紧张和解决是音乐的核心。然而对于普通的音乐听众,并没有一个非常可靠和很深的堆栈。在巴赫的《和声小迷宫》中,听众就很容易在急剧变化的调子中迷失方向。

语言中也存在递归,我们在理解语言时,大脑也在维护着一个心理堆栈。作者这里给出了一个递归迁移网(RTN)的例子,展示构造自然语言时也有递归过程。递归可以是自己调用自身,或者借助两个过程相互调用,但一定有一个终结条件,避免递归进入无限循环。

之后,作者由用递归的图像模式给出了和斐波那契数列的联系

  • 图像的递归模式:G(n) = n - G(G(n-1)), G(0)) = 0
  • 斐波那契数列:F(n) = F(n-1) + F(n-2)

在递归的规则复杂起来之后,就不再能那么明显地感受到斐波那契这样明显的模式。比如Q(n) = Q(n-Q(n-1))+Q(n-Q(n-2))这种。从磁场强度和粒子变换中也能看到同样的递归规律。我们在重温下递归的意义——递归是在不同层次上出现“同一”事物,但它们又不是完全相同的。这些事物有着映射关系,虽然它们不完全相同,但有着相同的功能关系让每次递归看起来“风格”相同,就像艾舍尔的画一样。

在弈棋程序的超前搜索树中,也有着递归的过程,AI在计算下一步的时候,要站在对方的角度考虑走一步之后如何计算,这个递归足够复杂时,就像上面的Q(n)序列,AI变得难以找到一种模式,体现出智能的特点。程序甚至可以设计成可以修改自身,或者修复、扩展自身。递归的神奇特点在侯世达定律中也有体现:

做事花费的时间总比预期要长,即使你在预期时考虑了侯世达定律

音程增长的卡农

小品以俳句为引子,找到了BACH音程和CAGE音程的联系,巧合的是Bach和Cage也是两个音乐家的名字。

意义位于何处

信息、意义、遗传型、表现型、自动唱机、外在消息、内在消息

这章主要聊了意义是一条消息所固有的,还是智能或是机器和消息交互后产生的。首先,意义被提取的过程一定有一个信息携带者,等待信息揭示者发现某种同构关系来提取或解释消息。就像唱片的槽纹模式和声音的同构被唱机提取出来一样。DNA的遗传型和表现型也是相似但又不完全一样的例子。相对唱机演绎声音,DNA表现遗传特征要复杂了太多太多,以至于没有人能讲明白之间的关系。因此唱片和声音的同构可以叫做平凡同构,DNA的遗传型和表现型间的同构可以称作异常同构

我们先看唱片,音乐并没有隐藏在唱机中,而且我们也能以任意精确程度去匹配唱片的片段和输出的音乐,唱片和唱机也能很好地分离开。这么看,消息的意义似乎是固有的,唱片所蕴含的意义不随唱机转移。再看DNA,情况不一样了起来,DNA必然也包含了遗传信息,但是它依赖必须的化学环境来表现这些遗传信息,缺少了环境,DNA的意义就不能被揭示出来。

唱片的意义在于播放时,声音激活大脑中的适当结构,激发听者的一些情感。似乎对于唱片的释读早就内置在我们的大脑里。如果信息在本质上具有足够的内在逻辑,一旦遇到足够智能的生物,环境就能自动的建立起来。这样的话,我们就可以说消息的意义是一种固有性质。在这个假设下,信息可以分为3层:

  • 内在消息:理解内在消息就是抽取发送人要传递的意义
  • 框架消息:就是确认需要一种解码机制
  • 外在消息:理解外在消息就是建造或知道如何建造能够解译内在消息的解码机制

消息必须具有某种触发器一样的东西,传递出它包含有意义,就像罗塞塔石碑上的重复符号一样。从某个角度看,欣赏音乐、艺术作品也是一个解译内在消息的过程。其中作品传递的风格和外在消息以及解码技术表述的是一件事情。外在消息的“触发器”能否被人发现是无法预知的,在消息内写上“这是一条需要破译的消息”也是不行的。外在消息的本性就决定了它们无法被任何显式语言所传达,解码过程是个启发式的,就像是涌现出来的一样。

这么看来,消息内的意义能否被发现,和解读它的智能关系甚密,譬如发现唱片内的螺线型沟槽又或者发现图案上使用了日文字母。智能似乎自带发现外在消息触发器的能力,并且一旦发现就可以对这些消息进行解码。在这个基础上,如果我们承认意义是消息所固有的,只等着智能去发现,就是在承认消息的释读机制是具有普遍性、自然界中的基本形式。然而,这样的话分布在宇宙各处的智能生物对于消息处理也要具有不变性。这显然有些人类沙文主义或是地球沙文主义。我们无从知道其他智能会不会像人类智能一样解读我们看起来很显然的消息。

如果我们要像外星智能传达消息表达斐波那契数列的规律信息,仅仅在金属板上蚀刻2个星号,肯定不如刻上前n项更容易暴露触发器。后者的长遗传型传送的外在消息更多,这就使内在消息得以被读出。再举个例子,我们往外星发送巴赫和Cage的唱片,Cage的曲子就难理解了许多,因为能够欣赏Cage作品需要熟知固定音乐在近几十年的发展轨迹,这样的听众本身就内赋了作品的独立反叛意义,唱片只是简单起个触发作用。而这仅限于地球。相反,讽刺的是,欣赏巴赫的作品就远不需要那么多文化知识。这也体现出了奇妙的一点:智能喜好模式化,厌恶随机性。回到DNA的例子里,要恢复遗传型蕴含的信息,需要极大程度地理解DNA需要的环境,才能完好地表现出表现型。那么,在恢复的过程,什么时候才能说已经达到或者隐含了表现型呢?这在某些国家是个高度敏感的话题(堕胎)。

半音阶幻想曲,及互格

小品里阿基里斯尝试用命题演算证明乌龟所说自己的壳是/不是绿色的矛盾,这也引出了下章的讨论内容。

命题演算

命题演算、形式化、半解释、元理论

小品里阿基里斯朴素的命题演算并没有说服乌龟,这章用符号化的形式系统来模拟命题演算。我们用P、Q、R这样的符号表标识一个原子,将由~<>符号和原子按指定规则组成的串成为良构串(即命题)。同时给出合并P∧Q是定理可以得到PQ都是定理)、分隔PQ都是定理可以得到P∧Q是定理)、双重反转规则作为串生成的基本规则来产生更多良构串(命题)。对于包含符号的串,也有一个幻想规则

预设一个良构串X,把它当成定理,根据已有规则推导,生成另一个良构串Y,那么可以说X就是Y的前提,Y就是X的结果。记作<x->y>

在这个基础上,借助双重反转规则,可以很容易得到<P→~~P>这样的定理。在幻想规则的层级不止一层时,还有一个搬入规则,即在一个“幻想”中,更高层级“幻想”里的定理都可以拿来并使用,借助这个规则,又可以生成<P→<Q→<P∧Q>>>这样的定理(可以发现,这正是分隔规则的符号化,幻想规则就是命题演算的符号化过程)。

当然,除了上面这些规则,还有很多别的:

  • 分离规则:如果x<x→y>都是定理,那么y也是定理
  • 异位规则<x→y><~y→~x>是可以互换的
  • 德摩根规则<~x∧~y>~<x∨y>是可以互换的
  • 思维陀螺规则<x∨y><~x→y>是可以互换的

可以很容易发现,~<>这些符号和逻辑符号的关系。从上面规则生成出来的定理也可以轻松对应现实世界中的逻辑命题。当一个人解释命题演算里的定理,而不解释其中的原子时,作者称之为半解释。例如P∨~P可以半解释为P或者非P。可以发现,命题演算中生成的定理被半解释时,会被人们视作普遍为真的句子。

在命题演算的帮助下,可以用印符规则生成普遍为真的语句,这些宇宙里的核心真理似乎是有规律的。在上面的解释下,命题演算里的定理都是真语句。那么我们能证明这一点吗?又或者说,我们知道系统是否一致吗?是否有可能有一个定理x按照规则推导可以得到~x。这个问题的证明等效去证明上面那些规则,可以想到,随之而来的还会有证明的证明,证明的证明的证明……最外层系统总是需要一个未经证明的假设,是凭我们的信仰接受的。就像阿基里斯和乌龟谈话里,要从<<A∧B>→Z><A∧B>证明到Z,需要先证明符号化的<<<<A∧B>→Z>∧<A∧B>>→Z>

让我们再回头审视那些规则,我们在用推导出来的定理图式作为导出规则生成其他定理时,使用的是W方式,这种关于命题演算的理论可以叫做“元定理”,即关于定理(可导出的串)的定理(被证明的结果)。在使用W方式时实际上已经失去的系统的形式性,W方式是在系统之外的。只有J方式是在系统里的。然后要生成更多的定理,必然需要这种“捷径”。即使把元理论抽象成形式系统,也依然会有元元理论去加快命题演算中的定理推导。

那么一个推理理论能否包含自己的元理论呢?事情并不简单,因为系统“对自身思考”时,实际上也是运用了W方式站在系统外边做的观察。然而后面的章节里会提出一个系统可以达成这样的目标,当然,在推演时也是要十分小心J方式和W方式的不同的。

命题演算很像以某种方式进行的推理,但是推理的规则和人的思维是不一样的。一个证明是非形式化的东西,是思维的产物,本身包含了思维各种复杂的特点,最终“感觉是对的”。而推导是证明的人造对应物,用意是去使用逻辑结构形式化达到同样的目标。任何一种简单性都会带来一种特殊类型的复杂性。就像证明就依赖了人思维语言的复杂性。书中在这里使用了<P∧~P>构造出<<P∧~P>→Q>这样一个矛盾。在数学中,人们通常通过修正系统来应对矛盾。譬如在这里可以通过限制幻想规则使用要求新增命题的关联性来限制Q的加入。后一章中通过构造一个更大的系统,来将这种不完全或不一致从子系统的命题演算错误转移为较大系统的毛病。

螃蟹卡农

小品借互文的形式(即全文对称)致敬的巴赫的螃蟹卡农作品,同时螃蟹提到的DNA双螺旋结构也和互文有呼应。此外引入DNA里的CGTA碱基对,和螃蟹(Crab)、基因(Gene)、乌龟(Tortoise)、阿基里斯(Achilles)的首字母一一对应,实在妙绝。

印符数论

TNT、自由变元、量化变元、ω不完全系统、形式推理

印符数论(TNT,Typographical Number Theory)预期把数论形式化在印刷符号中。在TNT中先要定义一些原子;

  • 数字:0、S0、SS0、SSS0,其中S标识是它后面那个东西的后继
  • 变元和术语:用a、b、c、d、e这样的小写字母表示不确定的数,+和·标识加和乘,=表示相等
  • 上一章命题演算中的符号也可以拿来使用

这样我们就能构成一些基本的句子了,句子一般都有真假。但像$(b+S0)=SS0$这样的则不具有,因为其中有自由变元b,这样的公式也被称为开公式。使用量词可以将开公式转化为可以判断真假的闭公式。譬如$∃b:(b+S0)=SS0$这样的存在断言和$∀b:(b+S0)=SS0$这样的全称断言。在量词管辖下的变元称为量化变元。结合上量词,我们可以表示更加丰富的数论命题。譬如,“5是素数”可以表示为:

$$~∃a:∃b:SSSS0=(SSa·SSa)$$

现在我们已经能形式化地给出命题的串描述了,我们怎么判断他们的真假性呢,即给出一个良构串的形式。首先看良构串的部件:

  • 数字:0和前面加上了S的0
  • 变元:a、b、c、d、e或者字母上面加'的形式
  • 项:数字或变元或前面加了S的项,如$SSa’$、$S(Sa·(Sb·Sc))$,根据是否有变元,项可以分为确定项和非确定项

使用上面的部件,下面是一些组成规则:

  • 原子:如果s和t是项,那么$s=t$是原子
  • 否定:前面加了~的良构公式也是良构的
  • 复合:x和y是良构公式的话,那么<x∨y><x∧y><x→y>也是良构
  • 量化:如果u是变元,x是一个包含自由变元u的良构公式,那么$∃u:x$和$∀u:x$也是良构公式

这样我们就得到了可以判断真假的TNT良构公式形成规则表,举例:不同的自然数有不同的后继可以表示为$<∀a:∀b:<a≠b→Sa≠Sb>>$。当然,用这样原始的形式表述“b是2的某次方”这样的命题时还是很吃力的。不过最起码现在已经有了一个基础。在这个基础上,我们要补充公理和推理规则,首先,我们将命题演算中所有规则接纳进来,之后给出下面5条公理:

  • 公理1:$∀a:~Sa=0$
  • 公理2:$∀a:(a+0)=0$
  • 公理3:$∀a:∀b:(a+Sb)=S(a+b)$
  • 公理4:$∀a:(a·0)=0$
  • 公理5:$∀a:∀b:(a·Sb)=((a·b)+a)$

1是关于0的性质,2、3关于加法、4、5关于乘法。看起来和皮亚诺公设很像,但并不一样,作者用鬼神的比喻给了出来:

  • 怪物是一个神怪(0是一个自然数)
  • 每一个神怪都有一个元(所有自然数都有后继)
  • 怪物不是任何神怪的元(0不是任何自然数的后继)
  • 不同的神怪有不同的元(不同自然数的后继不同)
  • 如果怪物有X,并且每个神怪都把X传给它的元,那么所有神怪都得到X(数学归纳法)

之后,我们可以继续给出一些可以操作TNT元素的规则:

  • 特称规则:如果u是串x中的一个变元,且$∀u=x$是一个定理,那么x也是定理,而且任意替换x中的u为其他串也都是定理,但要保证替换规则一样,且替换u的项不能包含在x中被量化的变元
  • 概括规则:和特称规则相反,我们可以把全程量词放回到定理中,即x是定理u是串x中的一个变元,那么$∀u=x$是一个定理,当然,在幻想规则中,不允许对自由出现在幻想中的变元应用概括
  • 互换规则:如果u是一个变元,那么$∀u:$和$∃u:$在定理中任意位置都可以互换
  • 存在规则:一个项如果在定理中出现任何次,那么可以用一个还未出现在定理中的变元替代,之后在最前面补充上它的存在量词,例如$∀a:Sa=0$可以把里面的0替换成$∃b:∀a:Sa=b$

围绕=还有下面这些规则:

  • 对称:如果$r=s$是一个定理,那么$s=r$也是一个定理
  • 传递:如果$r=s$和$s=t$都是定理,那么$r=t$也是定理
  • 后继:如果$r=t$是一个定理,那么$Sr=St$是一个定理;相反如果$Sr=St$是一个定理,那么$r=t$是一个定理

在上面这些基础上已经可以推导出很多基础但丰富的定理了,不过一些尝试发现,这个体系还缺少了概括的能力。例如,我们可以从0+0=0,推出0+S0=S0,再继续往后推,但却没法得到$∀a:(0+a)=0$。这时我们急需一个规则:如果这样一个金字塔的所有串都是定理,那么用来概括它们的全称量化的串也是定理。然而这没法在J方式下使用,它是在系统外思考的W方式。这种系统的缺陷被称为ω不完全性。一个看起来很显然的定理却没法在ω不完全系统中证明。就像非欧几何里面也是对未完全定义的“点”和“线”不补全了定义一样。

我们补上一条归纳规则:设u是一个变元,X{u}是一个u在其中自由出现的良构公式,如果$∀u:<X{u}→X{Su/u}>$以及X{0/u}二者都是定理,那么$∀u:X{u}$也是一个定理。这样整个形式推理似乎补全了。我们可以用彻底形式化的方式来推理,当然这么做是十分费力的,结合W方式会让它更好用。那么要证明TNT系统的一致性,有没有可能有比TNT系统更简单的方式呢?结果是没有。最起码要和TNT一样完全。

一首无的奉献

小品借由禅宗的探讨,对系统完全性和一致性上做了探讨。禅宗中的无门和和对佛性的追求和围绕系统一致性和完全性的追求也有共同之处。

无门和哥德尔

无门、整体论、WU谜题、哥德尔配数、编码

禅宗的基本教条就是:没有任何方法能刻画禅宗是什么。词语和真理是不相通的,至少词语无法捕捉到真理。文中对于禅宗公案的展示,表现出禅宗对二元论的反对,推崇整体论。而对词语的普通使用,甚至包括自己的知觉都表示你把自己当做世界当中的一个客体,从而远离了“道”。然而禅宗也是一个系统,不可能成为自己的元系统,总有东西处在禅宗之外,是无法用禅宗完全了解或说清楚的。

这时,我们回头看WU谜题,我们把WJU中的J看成1个,起始定理是1个洞,要判断是否有没有洞的情况。WJU系统中的4条规则分别:

  • 规则1:不改变洞的数目
  • 规则2:让洞的数目翻倍
  • 规则3:让洞减少3
  • 规则4:不改变洞的数目

由此看到,仅借助规则2和3没有办法让洞从1个变为3的倍数,自然无法推导出WU的答案。然而并非所有谜题都像WU这么好解。借助哥德尔配数,我们可以把所有形式系统的问题都转为数论描述,从而可以使用更为强大的数论。以WJU系统为例,我们可以把每个符号映射为1个数字,按照形状相似分别可以:

  • W <-> 3
  • J <-> 1
  • U <-> 0

那么,31就是起始的公理,规则1、2、3、4也分别可以用数字规则描述,譬如规则1可以表示为31后可以追加1,即31 -> 31 * 10 + 1。这样用哥德尔配数表示出来的形式系统就具有二重性,一重是印符规则的理解,一重的数字背后的数论理解。印符和算术规则是可以等价的。

再联想我们上节中的TNT系统。「30是一个WJU数」也可以翻译成TNT记号(尽管它极其复杂)。同样地,这个TNT符号也有印符和数论的两重意义。有人可能会说理解哥德尔配数后的编码需要额外知道编码的知识,但只要是无损编码,现实中不存在什么未编码的消息,只有我们熟悉和不熟悉的编码。

形式化表述数论的TNT系统本身也可以哥德尔配数,将TNT中用到的原子符号都使用数字组成的密码子表述,类似$(S0+S0)=SS0$也可以用数字表述出来。从而一串数字是不是一个TNT数的另一层含义就是一个数论语句是不是一个TNT定理。这也导致了一个结论:TNT的一个串有一个数论中的解释,而数论中的一个陈述的第二意义是TNT的一个陈述。

那么我们现在要找到一个TNT串,称之为G,它是关于它自己的,具体来说G的被动含义是「G不是TNT的定理」(当然G也有哥德尔配数的数论含义)。那么我们只要创造出这么一个G,根据TNT的完备性,G是一个真理,则「G不是一个定理」,反之亦然。这个TNT串反应的某个自然数的算属性值无法被TNT确定既真又假。从而违背了一致性。

上篇结

2021-2022: https://shenlvmeng.github.io/blog/2021/12/22/2021-to-2022/

2022比想象中过去得要快,这一年是紧贴时代脉搏一起脱轨的一年。生活的很多方面也不免受到影响。在日常琐事之中也埋着个人命运的一条主线——告别单身,走向家庭。在上面两个基调的影响下,回头看看过去1年拍过的照和写下的博文,似乎比往年少了些浓墨重彩的趣事。也许生活本身的难以预料琢磨,才是它令人着迷的地方吧。

2022回顾

书籍阅读上,经过大半年的努力,完成大部头《代码大全》的阅读;同时,神书《GEB》在进行中。读书进度受疫情影响较大,经常因为居家办公而打断。

小说的阅读倒是没受到丝毫影响,剩余的推理和刑侦小说一本不落的看完了。去年未完成的X、Y、Z的悲剧还有《犹大之窗》比想象中出彩。去年剩下的管理学书籍在工作中实在有些屠龙之术的处境,对于我这样的入行年限,研究这么高深的管理学技巧有捡了芝麻丢了西瓜的嫌疑。所以先暂时搁置。一项全新领域的研究也在《GEB》的阅读中有一定收获,其关于自我指代、悖论的介绍既艰深又有趣。

今年的黑天鹅事件比去年只多不少,先是3月份魔都疫情爆发导致封城3个月到6月初,我也是既20年在湖北隔离2个月后,体验到更久禁足的滋味。在家早起抢菜、做饭洗碗、白天下楼排队核酸、晚上跟着刘畊宏上下翻飞,快递动辄等个数月,数个节假日都只能在家庆祝。这种脱轨的生活在相册里体现就是清一色的做饭返图,在身体上体现就是精打细算的吃喝和锻炼让体重最多减了10斤,在竞技技术上体现就是出色的分手厨房技术,在脸上体现就是我已经记不起嘉雯化妆之后的样子。管中窥豹,可以想见我和嘉雯天天都在做些啥。因为这个原因,2022年3个硬菜的目标算是超额完成,卤牛肉、卤蛋、炸鸡柳、糖醋排骨、葱油拌面、鱼汤、骨头汤,这些以前不会尝试的大菜也算是给隔离时光带来了些许挑战的乐趣。隔离在家的3个月也算是部分弥补了之前异地的4个月里我和嘉雯聚少离多的场面。每天低头不见抬头见的同居生活,似乎并没有让我俩觉得枯燥,反而过得有滋有味的,属实奇妙。

待6月初接触禁足后,车还没骑过,2022业已过半。扑朔迷离的疫情形式和管控手势下,长途骑行和旅行也成了开盲盒。本来打算在国庆来一次上海到宁波的旅行,由于凯哥的婚礼被迫取消,2022也因此没了唯一的长途骑行机会。不过,能带着嘉雯参加我最好兄弟的婚礼,还是很棒的,基友也一同去了邵东。我和嘉雯还趁着机会见缝插针在长沙和杭州玩了一圈。这趟旅途也是意外满满,看来2022并不适合旅行。livehouse和旅行一样,因为疫情并未履行。饮酒频率在封控的上半年,作为苦中作乐的方式,一直保持不高不低的频率。在凯哥婚礼的晚上俩人喝high了导致吐个没完差点影响旅行后,一直戒酒至今。运动上,骑行和游泳的频率比去年有所下降,早睡早起也间歇性遵守。出外娱乐上,我俩还是以吃为主,但更多是熟悉的老店,可能是疫情让人变宅,探店的兴致也受到了影响。

每年都会有新的兴趣,2022也不例外。疫情在家莫名其妙燃起了桌游的兴趣,通过视频了解了美式、德式各种桌游的风格,隔三差五屯起了桌游。一度每个周末要组织2场桌游局,快乐的同时感觉大脑和嗓门也得到了充分的锻炼。为此家里还专门置办了一个书柜,其中1/3用来放我买回来的桌游。另外,我俩对于拼图的兴趣也让我们陆续买了3件2000片的大幅拼图,看着整幅画面从最开始的零散到逐渐完整,成就感满满。社交上,在那些桌游局中认识了不少嘉雯的朋友。中秋时候带嘉雯回了趟老家,相处得也很是愉快。因为老大哥尚在北京出差,去沈阳的计划没能成行。

工作上,似乎和之前有了不一样的认识,在转换了工作角色和工作位置后,渐渐找到了新位置的感觉。虽然和之前在教育的时候很不一样,但也胜在踏实和轻松。尽管团队规模也不算大,但也在尝试之前没有做过的东西,且有一定的学习和产出,整体还是朝好的方向发展的。回头想想自己去年的定位,可能有点过于浮躁和眼高手低了。在公司的定位下,还不需要我有多高的管理技巧,而需要有这种技巧的位置,我是否真得准备好了呢?看看现在扮演这些角色的人,他们的一天是不是你想要过的呢?这么想完之后,我又觉得现在这样也挺好。作为一只下金蛋的鸡,能保持自己一直下出金蛋也不错。至于下一步的计划,不能心急,还要慢慢积累。去年说的脚踏实地的状态,大概就是我现在的态度。我觉得这不算躺平,也算是在付着应付的责任。

时间来到12月,防疫政策陆续放松,情理之中又在意料之外,大家恢复自由身的同时,医疗和药物资源的挤兑也带来了一些困难。谢天谢地,在药品并不齐备的情况下,我和嘉雯的家人暂时扛过了第一波疫情高峰,成为了阳康。期待明年春暖花开之后,生活秩序恢复到疫情前的模样。

2022小结

  • 继续脚踏实地
  • 身体才是本钱
  • 只要方向是对的,好事就会不疾不徐地发生
  • 不要太“躺”,去做成一些事情,再小的也行
  • 两个人在一起久了之后,就成了命运共同体,很多事情要一同经历一同解决
  • 大脑像水车,书籍像水,只有不断往水车上浇水,才能保证大脑新鲜灵活

2023畅想

23年有些明确的路径,譬如求婚、领证、介绍双方父母认识,还有欠下的长途骑行和旅行等等。其他的就让它自然发生吧。

  • 家庭
    • 接待父母来魔都1次
    • 回家看望家人1次
    • 求婚成功
    • 领证
  • 个人
    • 完成GEB的阅读
    • 为之后的身份转变体验积累知识
    • 升级对象的骑行体验
    • 控制体重在当前附近
    • 坚持骑行和游泳
  • 生活
    • 至少1次户外长途骑行
    • 至少1次旅行
    • 看一次livehouse
    • 继续保持探店的热情
  • 事业
    • 在划定方向上完成里程碑
    • 所在小方向上为团队每个成员确定定位

剩下的,就让它顺其自然吧。我们明年见。

Bye~

后疫情时代,旅游消费也带上了运气成分,再没有“说走就走”般的潇洒

序:疫情

疫情管控下,旅行如同开盲盒。能去哪里玩耍并不由自己的财力喜好决定,也要仰仗病毒大人给面子。上海的病毒就格外不懂事,在打工人上班的工作日岁月静好,一临近节假日就格外来精神。中秋假期,和嘉雯就和病毒擦边而过,居家办公数日得以回家过节。这次国庆前也是提心吊胆,所幸所在区疫情未扩大,倒是一路辗转,核酸7天7检,在病毒边上跳舞。

这次选定长沙主要是因为姚凯婚礼,我和圣僧也是一起放弃了长途骑行计划,来邵阳蹭蹭喜气。来都来了,就顺道去长沙看看,刚好嘉雯也没去过。

Day 1 婚礼

邵阳

我们10月1日下午从上海南站出发,搭上前往邵阳的卧铺,嘉雯之前还未坐过卧铺,适应也挺快,躺床上一路晃晃悠悠,第二天一早便到了目的地。车窗外一片阳光普照,甚是美好。

邵阳

邵阳是个县级市,类似母亲老家枣阳的感觉。城市不大,尽管姚凯预定的酒店在城市的边缘,滴滴快车开了10多分钟就到了。在酒店大堂正巧碰到姚凯和伴郎在拍摄婚礼用的短片,人逢喜事,红光满面。我和嘉雯不便打扰人家的大喜之日,简单寒暄几句,住进了姚凯事先定好的房间。

酒店

中午的婚礼现场很完美,主持人沉着冷静,凯哥表现可圈可点,整个流程也没有让人尬到脚趾扣地的情节,现场一片其乐融融,像是我未来的理想婚宴。婚宴过后,姚凯忙东忙西,我和嘉雯找了个号称邵东环境第一的网吧,吸了2个小时二手烟。屋内空调还坏了,老板一人发了根老冰棍想让我们冷静下来。

在烟雾缭绕中拿到了首胜,做够了神仙,我们仓皇逃出,往市中心的百富广场走走。边走边走马观花地领略这里的风土民情。红绿灯对于这里的行人约束力并不强,大家秉承着实用主义至上的思路。路上无甚可逛,倒是见到了许多山寨品牌。晚上姚凯攒了个小局,可能是近2年不见,我俩喝得很尽兴,以至于我上头到夜里2点才睡下。

Day 2 美食

黄兴路、茶颜悦色、坡子街、南门口、电动车、东瓜山

从邵阳去长沙的火车很早,一大早被嘉雯喊起来,胃里还是酒意,头脑天旋地转,脚如腾云驾雾,8点的火车,我在车上睡了一路,出站后又吃了个酒店楼下的杀猪粉,算是恢复了大半元气。杀猪粉里的胡椒粉放得很有诚意,一碗下来喉咙和胃里都是暖暖的。

杀猪粉

来长沙的第一天,天气其实算是相当阳光明媚了。哦不,甚至是明媚得过了头,到了不打伞会晒伤的程度。也许是这个天气,又或许是郊区出现的零星病例,黄兴步行街上游客并没有想象中那么密集。作为长沙第一天的第一站,也是做好了敞开了胃吃一路的心里准备。这趟美食旅程从茶颜悦色自由开始。

茶颜悦色-1

长沙街头茶颜悦色的密度相当大,而这个相当大的极值恰在黄兴路步行街。本以为要排长队的奶茶店,在三步一茶颜的密度下,极大的提升了赏味体验。第一次我们尝试了幽兰拿铁和花木兰,风味果然名不虚传,恰逢活动两杯24,性价比拉满。坐在阴凉下抿一口,哈———,巴适。

幽兰拿铁

在等位中间,我们还尝试了大众点评推荐的刘记特色里脊肉,口感滑嫩,但口味偏咸,稍有些失望。中午我们顺道去了附近的德天顺盖码饭。招牌的孜然牛肉还有辣椒炒肉味道尚可,不过性价比略低。好好拍的话,应该还挺好出图。

盖码饭

吃完不像午饭的午饭,正赶上日头最毒辣的时候,在嘉雯的提醒下,臭豆腐是不可少的。我们顺道走到了坡子街,去了一家也是连锁店遍地的臭豆腐网红店,用了张优惠券买了个大份。俩人像砌墙一样,一人一块用臭豆腐砌到了嗓子眼。不得不说,从辣度上说,还是挺正宗的。

臭豆腐

既然吃了辣,解辣的奶茶自不能少,我俩浅排了个小队,坐在费大厨的门口,假装等号,享用着第二顿的茶颜悦色。栀子生椰就像涓涓细流,从墙砖的缝隙中滋润火热的胃。臭豆腐垒成的砖墙瞬间融化,胃口又找了回来。

栀子生椰

尽管已经不知道吃了多少,在坡子街走了走之后,我还能吃的壮志又回来了。路上遇到M记新品——红糖冰粉,入之。刚到长沙的这几个小时似乎一直在吃,但我发现不止是我,步行街上的人们也都化身饕餮。

红糖冰粉

吃

下午沿着步行街一路走到南门口,兜兜转转好不快活。不过直到第3天,我俩才发现步行街的地下一样精彩。

柠檬茶

不知不觉,来到南门口的时候,时间已近5点,我俩在日头地下行色疲惫,想找个能歇歇脚吹吹空调的地方。秉承着光荣传统,我们骑着电动车去了网鱼网咖,并用充值体现我们对这家连锁企业的支持。

嘉雯是第一次骑电动车,我虽是第二次,也比她强不少。只见她最开始慌慌张张,拒绝相信电动车有手就能骑比自行车还简单,没骑到几分钟后脸上就逐渐浮现出笑容。“啊,真香。”,我想她一定想这么喊出来。

从网吧出来日头已经落下,我们要去寻找晚饭的觅食地点。才骑过一次电动车的嘉雯果断选择骑着去。骑电动车的简单快乐魔咒一定是她来长沙的最大收获。不过这可能也和养猫养狗一个道理,别人家的猫狗肯定比自己家的香,毕竟不用自己喂粮、洗澡、吸毛、铲屎就可以撸,只需办张6块钱的卡就能3次免费骑到不用自己充电的电动车,这也太爽了吧。看嘉雯掩饰不住的笑容就知道。

电动车

拐到湘江中路后,便来到了风景最好的一段,虽然看不到江,但是一路晚风吹着,一边江边步道上不少市民游客漫步起舞,另一边高楼彩灯鳞次栉比迷乱人眼,这种不受约束自由的快乐可能正是旅行的一大意义吧。

高楼

在意外发现馨砣粉店只有早上营业后,我们正巧赶上东瓜山夜市的时间,可能是晚上气温宜人,本就狭窄的东瓜山街头人头攒动,路边的摊位卖着酒酿汤圆、紫苏桃子、糖油坨坨,饭店多是烧烤,赤膊和半赤膊的人们流着汗翘着腿坐在长条板凳上,撸串碰杯,交谈混着叫号声满是喧闹。打开大众点评搜索,附近网红店,高分店不胜枚举,选择困难。不由得感慨,长沙这座城市,不论白天夜晚都是映入眼帘的小吃文化,不多带1个胃来,怕是得多带好多关于美食的遗憾离开。

由于来的晚,加上对密集人群的天生厌恶,我俩并没有去朋友推荐的小龙虾店,将就选择了一家有空调的烧烤,对付着解决了晚饭的问题。下次要是再来,还是要赶早点,去那家天宝兄弟探探究竟。

东瓜山

饭后,我们骑上电动车,沿着白沙路——韭菜园路一路直行回到酒店。路上还经过了天心阁。可惜时间有限,只是远远拍了张剪影。

天心阁

Day 3 徒步

糖油坨坨、橘子洲、岳麓山、冰箱贴

由于前一夜空调不给力加上睡觉不踏实,早上起床就发觉有些感冒。想到晚上看到的望城区和天心区有病例,细思恐极。再加上西伯利亚远道而来的冷空气前锋已经来袭,天色阴沉了许多。实在是有点打扰游览的雅兴。洗漱过后,我们打起精神,先去太平街区转转。这里有点像宽敞版的南锣鼓巷,也许是疫情也许是天气,街头的人不多。我们叫了个糖油坨坨,垫了点肚子。

糖油坨坨

浅走了一段,在西牌楼的街上看到了传说中的公交新村粉店笨罗卜湘菜。粉店环境一般,食客即吃即走,排队的人尚不算长,湘菜店则排好排到几百桌。我俩叫了雪里蕻牛肉粉,感觉不过如此,甚至不如酒店楼下的杀猪粉。考虑到价格10元出头,我们也没有太过抱怨。

公交新村粉店

吃饱喝足,我们沿湘江大桥走向橘子洲,恰如我7年前第一次来长沙那样。不同于那次8月底七月流火尚有暑热未散尽,此时桥面江风尽是萧瑟秋意,昨日的烈日高照完全不见了踪影。橘子洲角的绿树和江岸的高楼相互应和,我一边拍照一边握紧手机。

湘江-1

湘江-2

沿着桥面的方向向对岸望去,岳麓山的曲线格外明显,那里正是我俩下午要去的地方。

岳麓山

在江中间下桥便到了橘子洲。下了桥看到核酸点排队人不多,为了保险,我们顺带做了个核酸(没想到第二天救了我们)。时间不充裕的情况下,想在橘子洲头看伟人头像和指点江山题字,小火车是必坐的,40元不限次数还算良心。只可惜头像周围的人实在太多,没有办法拍下我们仨的合影,我和嘉雯也不想和围栏边密密麻麻的人群一起凑热闹,只是坐在江滨的台阶上安静吹风。

头像

广场

坐太久不利于感冒康复,兴尽我俩又坐地铁来到湖南师大,准备吃个午饭再从岳麓书院方向爬岳麓山。没想到这里的人比昨天还要夸张,不禁感慨防疫的困难程度。最终花了1个小时终于拿到茶颜悦色和M记。

声声乌龙

爬山的过程其实比想象中更快,依旧是从书院出口沿爱晚亭上山,依旧是走一段石板路走一段野路,欣慰的是嘉雯终于买到了心心念念的冰箱贴。山顶并不算高,也就200来米,铺设的柏油路上走满了游人。此时天色已不早,我俩赶在缆车卖票的最后时刻买到了下山的缆车,真是好险。单人下山只需25元,能坐10来分钟,还在俯瞰橘子洲、湘江两岸,性价比很高。

缆车

下了山已经是5点多了,天色渐暗,我们在虎头局吃了俩泡芙,也没有到处走走的念头,想着去网吧坐一会儿再去IFS逛逛,没想到因为胜率问题,还没拿到首胜就已经9点。屋外气温已不适合短袖出行,我俩干脆打道回府。

Day 4 意外

黄码、绿码、IFS

早上起床,感冒没有好转,天气却进一步入冬,窗外下起了小雨,气温更低了。等来到地铁站准备前往五一广场时,发现健康码居然变黄了,这也是疫情这几天,健康码第一次变黄。想到最近3天也有2次核酸阴性,应该可以申诉转绿,于是回到酒店向所在社区报备,工作人员说可以再做一次核酸就可以变回来。我们便前往最近的核酸监测点做核酸,结果黄码的检测队伍已经排满了,只能去湖南第二人民医院去做。从队伍里的人聊天的内容看,健康码突然变黄的游客似乎并不少。

黄码

我们打了个车去往下一个核酸点,还好也就10分钟车程,令人哭笑不得的是,车还没到码就神奇地又变绿了。为了保险起见,我们还是打算做一次,正排着绿码核酸的队伍,健康码又变黄了。只能再去黄码队伍里排着,寒风里像我们这样排队的人并不少,也有挺多黄了又绿绿了又黄的。在网上搜了下黄码申诉的渠道,结果电话到最后都没有打通过。只能被迫下载极为难用的app,也不顾忌个人信息的安全问题,做了黄码专属核酸,填了申诉表单;填完也看不到申诉进度,只能祈祷黄码能早点转绿。在微博上搜了下#长沙 黄码#关键词,不出意外,受害者颇多,甚至也有当地人前来吐槽。

申诉

做完核酸也到了午饭的时间,户外实在太冷,也没有心情和健康码前往五一广场吃湘菜,只能就地在马路对面的巡湘记解决。期间又经历了一次黄转绿转黄。而我已风轻云淡,伴随着黄码和因感冒失灵的鼻子,这顿饭吃得并不算香。不过,其中的豌豆还是相当不错。

豌豆

饭后码依旧是黄的,参考小上的经验,我们去韭菜园社区线下申诉,幸运的是因为做了3天两检,我们的情况可以通过填表申诉解决,欣然配合后社区提交疾控中心,说2个小时内解决。在码转绿之前也没有商场能容下我们,我俩只能再次光临网鱼网咖。由于抵达时间过早,码还没有变绿网吧小哥还收到了社区电话,结果等我俩打车准备回酒店的时候,码突然好了。在给网吧小哥解释后,我们在这里战斗了一个下午。

晚饭时间,我们打车去了最后的目的地IFS,享用了最后的茶颜悦色,以一顿不辣的湘菜结束了第三天。

茶颜.jpg

Day 5 宅

杭州

绿码坚强地坚持到了这一天一早,我们6点不到便启程前往长沙南车站,一路和满口湘普的滴滴司机交谈甚欢。经过4个小时车程,我俩成功“逃离”长沙,来了杭州。杭州东站的检疫措施一点不输长沙,偌大的站厅被划成迷宫,好在核酸效率很高,只用了10多分钟我们就坐上了去江边的地铁。

杭州也是阴雨连绵,我们满心期待预定的江景房风景也打了折扣,江面和对岸的群山如同被盖上了薄纱。我俩感冒没好,也没有心情冒着雨去西湖散步,叫了个外卖奶茶吃饱喝足便回到床上躺下。嗯……果然还是茶颜悦色更胜一筹。

江景

晚上我们去附近的一家泰式餐厅南妈stay解决了晚饭,性价比还是不错的。

南妈stay

Day 6 返程

旅程的最后一天,我们睡到12点才出发,雨还在淅淅沥沥下着,雾倒是消散了些,旅行的兴致已经没剩多少。商城里的点都德还是熟悉的味道,各种点心和在上海还有广州尝到的区别不大。

点都德

饭后再商场里闲逛,发现居然有KTV,价格还算公道,于是在中包里度过了愉快的一个下午,唱到了许多许久未唱的老歌。

KTV

从商场出来,时间也不早了,我们打着伞,拖着拉杆箱,坐地铁来到杭州站。晚上9点多久回到了上海,还是熟悉的上海南站。补做了车站的核酸,顺利回了家,结束了这次惊喜意外的国庆旅程。

尽管和去年相比,因为核酸和天气多了意外,但也是我和嘉雯的第一次一起出发一起回家,期待下一次出发。坏天气总会过去,坏运气也是。

文接关于编码的一切 ——《代码大全》 下

软件质量

在了解最基本的变量和语句组织后,这一部分围绕提高软件质量展开。

概述

软件的外在质量特性体现在:

  • 正确性
  • 可用性
  • 效率
  • 可靠性
  • 健壮性
  • 适应性
  • 完整性
  • 精确性

内在质量特性体现在:

  • 可维护性
  • 可扩展性
  • 可移植性
  • 可读性
  • 可测试性
  • 可理解性

而改善软件质量可以有很多技术:

  • 确定目标
  • 测试策略
  • 非正式走查
  • 正式技术复查
  • 外部审查

开发过程中可以通过量化结果、制作原型、控制变更等手段提升质量。

  • 不同的质量保障手段效率各不相同,多种缺陷检测办法结合、人工和计算机检测结合,效果会比单打独斗要好;然而没有任何一种错误检测办法可以解决全部问题
  • 越早引入软件的问题,修正代价越大,尽早开始代码质量保障工作
  • 需求或架构上的错误相比编码阶段会带来更广泛的影响
  • 改善质量可以降低开发成本

协同构建

协同构建即在同行的帮助下完成代码构建。方式包括但不限于结对编程、正式检查、非正式技术复查、文档阅读等等。结对编程技术性复查可以缩短开发周期,排查出更多错误,减少代码的维护时间。同时,同行间的协同构建也有助于快速提升公司开发者的开发水平。

  • 结对编程
    • 结对编程中,一名程序员敲代码,另外一名注意有没有出现错误,以及考虑策略性问题
    • 成功秘诀
      • 事先确定编程规范,避免琐碎争吵
      • 不编程的成员不能变成旁观者
      • 不需要在简单问题上使用结对编程
      • 灵活对结对成员轮换,让大家熟悉不同系统
      • 避免新手组队
  • 正式检查
    • 一种特殊的复查,与会主持人负责组织事宜,按计划、概述、准备、与会人(非作者)阐述代码、详查报告、跟进等步骤推进
    • 针对代码而非作者
    • 最终由作者负责如何处理缺陷
  • 走查
    • 走查是形式和流程都比较宽松的代码复查方式,时间较短,重点也在检查错误而非修正它们
  • 代码阅读
    • 类似Code Review,关注点主要在代码本身,而非会议
  • 公开演示:类似showcase

开发者测试

测试分单元测试、组件测试、集成测试、回归测试、系统测试,前两部分通常由开发者进行,后三部分由专门的测试人员进行。测试按是否了解测试对象内部工作原理也可以分为黑盒测试和白盒测试。最后要注意,测试 ≠ 调试

对于开发者而言,测试天生有些特别:

  • 测试的目标是找出错误而非实现功能
  • 测试绝不可能彻底证明程序里没有错误
  • 测试无法改善软件质量,它本身只是一个指示器

开发者测试在整个项目时间中大概占8% - 25%,在测试时有一些tips:

  • 写代码前先写测试用例,不会比后写多花功夫
  • 不止进行“干净测试”,也要考虑“肮脏测试”
  • 很容易对测试覆盖率过度乐观

在现实世界中,要穷尽所有可能的输入是不可能的,测试不可能完全,有些方法会起到作用:

  • 使用路径数计算计算用例的最少数目,它可以保证所有代码的执行
    • 通过子程序时,开始记1
    • 遇到ifwhilerepeatforandor或等价物时,+1
    • 遇到每一个case语句,+1;没有缺省分支时,再+1
  • 数据流测试可以覆盖到数据的变化情况
    • 数据有已定义已使用已销毁状态
    • 子程序控制流有已进入已退出状态
    • 测试所有的已定义已定义-已使用组合,注意其他的组合顺序
  • 一个好的测试用例可以覆盖可数据数据的一大部分
  • 用启发式方法去猜测错误
  • 留意边界值,如数组边界的off-by-one错误
  • 考察好数据坏数据
    • 好数据:期望输入、最小正常值、最大正常值、旧数据兼容性
    • 坏数据:没有数据、过多数据、无效数据、长度错误、未初始化
  • 使用容易验证结果的测试数据

关于错误,也有一些先验的规律:

  • 符合八二法则、绝大多数错误通常和几个具有严重缺陷的子程序有关
  • 大多数错误影响范围有限
  • 大多数构建错误来自编程人员的错误,多从自身想问题
  • 笔误是一个常见的问题根源
  • 错误理解需求也是常见原因
  • 大多数的错误都较易修正
  • 业界经验是,平均1000行代码发现1-25个错误,发布产品大概是1000行代码0.5个,
  • 同样留意,测试用例本身是否有误

在当前的编程环境和工作条件下,容易找到很多测试框架,它们会包含脚手架、diff工具、测试数据生成器、覆盖率监测、日志记录、系统干扰器等。另外,自动化测试、测试问题复盘等对测试质量也有提升帮助。

调试

调试(debug)是寻找错误根本原因和纠正错误的过程。它和测试一样,本身不是提升代码质量的方法,而是诊断代码缺陷的手段。

  • 调试の误区
    • 纯靠猜测找到问题所在
    • 不去花时间理解程序和问题
    • 暂时性的掩盖问题
    • 把问题推给硬件,而不思考是不是出在自己身上
  • 寻找缺陷的步骤
    • 让错误状态稳定下来 => 稳定复现
    • 收集相关数据,构造错误原因假说
    • 通过测试或检查代码证实或证伪假说
  • 一个无法稳定重现的问题,可能和初始化或和时间有关系
  • 寻找缺陷的tips
    • 构建缺陷假说时,要能合理解释所有测试用例
    • 定位缺陷困难时,及时补充更多的测试用例复现问题,用多视图的方式盲人摸象定位缺陷
    • 测试用例过于发散时,及时用用例否定一些假说
    • 二分法缩小嫌疑范围
    • 检查最近修改最近出过错的代码
    • 小黄鸭调试法
    • 暂时休息一下
    • 蛮力调试
      • 抛弃有问题的代码,从头设计和编码
      • 抛弃整个程序,从头开始设计和编码
      • 不放过任何一个编译器错误
      • 手动遍历所有的循环
      • 更换编译环境或比那一起
      • 持续自动化测试
      • 显示代码中所有的打印日志信息
    • 给启发式调试法一个deadline
    • 调试中避免心理惯性:人们总期望一个新现象类似他们见过的某种现象
  • 修正缺陷
    • 修正问题前确保已经很好地理解了
    • 理解程序而不仅是问题
    • 验证对错误的分析或理解
    • 保留最初的源代码
    • 治本,而不要治标
    • 一次只改一个地方
    • 搜寻代码中还有没有类似的缺陷
  • 调试工具
    • 源代码diff
    • 编译警告信息
    • lint和代码自动修正
    • 性能剖测(profile)
    • 测试脚手架
    • 调试器

重构

更多重构介绍可以参考这篇blog重构 —— 代码的实用性与艺术性

重构即在不改变软件外部行为的基础上,改变其内部结构。即便是管理完善的项目,每个月都会发生需求变化,稳定不变的需求是个童话。

代码出现以下“坏味道”(smell)时,代表需要重构了。

  • 代码重复
  • 子程序冗长
  • 嵌套过深
  • 内聚性差
  • 参数列表过长
  • 类和继承关系不合理
  • 基本数据类型过多
  • “流浪数据”传递
  • 无所事事的类
  • 命名不当
  • 难理解的注释
  • 全局变量
  • 子程序需要前置或后置处理
  • 过早设计或过度设计

重构分级别有下面一些手段

  • 数据级
    • 具名常量
    • 更可读的变量
    • 函数替代表达式
    • 中间变量
    • 减少重复使用变量
    • 类型码转成类或枚举类型
    • 类封装
  • 语句级
    • 分解布尔表达式
    • 用可读名字的布尔函数替代布尔表达式
    • 合并条件语句中的重复代码片段
    • breakreturn替代循环控制变量
    • 多态替换条件语句
    • null对象替代空值检测
  • 子程序
    • 内莲花
    • 提炼子程序
    • 转化为类
    • 增/删参数
    • 合并/拆分子程序
    • 读写操作分离
    • 传递成员/类
  • 类实现
    • 值/引用对象转化
    • 成员函数/成员数据位置移动
    • 相似代码提炼到基类
    • 差异代码拆分到派生类
  • 类接口
    • 类拆分/合并
    • 删除类
    • 去掉中间人
    • 继承替代委托
    • 委托替代继承
    • 引入外部成员函数
    • 引入扩展类
    • 封装不使用的成员函数
  • 系统级
    • 为无法控制的数据创建索引源
    • 工厂模式
    • 异常/错误处理代码选型

要想让重构不影响日常功能开发,需要考虑

  • 有一个代码版本管理工具
  • 重构步伐小一点
  • 同一时间只做一个重构
  • 重新测试
  • 增加测试用例
  • 检查代码更改
  • 根据重构风险选择重构方法
  • 不要把重构当成糟糕设计的挡箭牌
  • 避免用重构代替重写

重构可以在修改代码的时候进行,不论是增加修改子程序还是类,或者是修复缺陷。对于从未重构的糟糕代码,可以用一部分混乱的代码隔离复杂度,把理想规整的代码和混乱不堪的真实世界隔离开。

代码调整策略

代码调整指出于性能考虑,对代码进行实现上的调整。本章主要讨论策略。

  • 动手前的考虑
    • 性能≠代码速度
    • 想清楚你确实在解决一个需要解决的问题
  • 调整考量
    • 程序设计:设计架构时考虑整体性能,再为每个子系统、类设计要达到的资源占用目标
    • 和操作系统的交互
    • 代码编译
    • 硬件
    • 类和子程序设计
    • 代码调整
  • 帕累托法则,又称八二法则,程序中20%的子程序耗费了80%的执行时间
  • 一些错误认知
    • 减少代码行数就可以减少资源占用
    • 特定的写法会比其他的更快,代码也会更小(要看编译环境
    • 应当随时随地优化(不成熟的优化不如不优化
    • 运行速度和正确性同样重要
  • 先提升代码可维护性,在程序完成且表现正确后,再去提升系统性能
  • 常见的低效率来源
    • IO操作
    • 内存分页
    • 系统API调用
    • 脚本语言
  • 性能测量:没有准确的性能测量就不要去做优化
  • 代码调整需要反复尝试,才能达到理想的性能提高

代码调整技术

文接上章,本章讨论具体调整手段。

  • 代码调整和重构相反,大多数情况下是以牺牲程序可读性为代价换取更高的性能
  • 调整手段
    • 逻辑
      • 知道答案后停止判断
      • 按照出现频率调整判断顺序,把容易为真的判断放在最前面
      • 表驱动法代替复杂表达式
      • 惰性求值
    • 循环
      • 把判断提出循环体
      • 展开小循环
      • 合并循环
      • 减少循环体内的操作
      • 用哨兵值提前结束循环
      • 把最忙的循环放在最内侧
      • 用低强度的计算代替高强度计算,如加法替代乘法,乘法代替幂运算
    • 数据
      • 使用整型而非浮点数
      • 减少数组维度
      • 使用辅助索引
      • 使用缓存
    • 表达式
      • 利用恒等式简化代码复杂度
      • 削弱计算强度
      • 编译期初始化
      • 小心系统函数,为了兼容最糟情况,系统函数会比较复杂
      • 事先算出结果
      • 删除公共表达式
    • 子程序
      • 子程序改为内联
    • 用低级语言重写
  • 再次强调,没有性能优化测量就没有代码调整

系统考虑

这一部分站在系统的角度考虑构建过程中的程序规模、集成、工具等问题。

程序规模带来的影响

软件规模的扩大可能会为你带来意料之外的大量问题。

  • 如果你习惯于开发小项目,那么你的第一个大型项目很可能会严重失控
  • 沟通交流:项目成员的扩大带来的交流路径不是加性的,是乘性
  • 错误:项目规模的扩大也会带来更高的缺陷密度
  • 生产率:大项目会带来更低的生产率
  • 工作量:软件构建的工作量和项目大小是线性关系,而其他活动的工作量则是非线性增加
  • 不同规模的代码从小到大可以称作程序、产品、系统、系统产品,没能意识到它们间的不同也是估算偏差的出现来源
  • 项目越正规,就越重视方法论,不得不写的文档也会更多,撰写的文档也会更正规

构建管理

构建管理是软件管理中的一部分。

  • 鼓励良好的编程实践
    • 逐行复查
    • 代码签名
    • 鼓励最佳实践
  • 配置管理:系统化定义项目工件和处理流程
    • 需求和设计变更
      • 遵循系统化的变更手续
      • 成组变更
      • 评估变更成本
      • 坏味道:频繁大量变更
    • 软件变更:版本控制软件
    • 机器配置变更:机器镜像
  • 评估构建进度表
    • 评估项目规模和工作量是软件项目管理中最具挑战性的部分,平均水平的大型软件都要超时1年,超预算100%才能完成
    • 清楚说明软件需求
    • 使用不同方法评估再对比
    • 定期更新评估
    • 以下因素会影响软件开发进度,但不易被量化
      • 开发者的经验和能力
      • 团队的动力
      • 管理质量
      • 可重用的代码数目
      • 人员流动性
      • 需求变更
      • 文档量
      • 分级安全环境
    • 如果进度落后了要怎么办
      • 扩充团队在项目任务不能分割并一一击破时,只会增加项目交流复杂度,并加速项目延期
      • 缩减项目范围,有限保证核心功能
  • 度量:对项目特征进行度量可以评估项目进度和风险,当然保证ddl比收集度量数据更重要
  • 把程序员当人看
    • 程序员1天大概有30%时间花费在“对项目没有直接好处”的非技术活动中
    • 不同程序员间的努力和能力差异很大,不同团队在软件质量和生产率的差异上也很大,好的和坏的程序员都倾向于聚集在一起
    • 在代码风格的信仰问题上,使用“建议”或大多数人达成统一的指导规范
    • 优良的物理工作环境有助于提升程序员的生产率和生产质量
  • 管理你的管理者:向上管理

集成

集成方式也会影响到集成的质量。从频率上分,有阶段式集成增量集成,在阶段式集成中,分为单元开发和系统集成两个阶段。增量集成中,程序是一点一点写出来并一点点拼接起来的。对比阶段式集成,增量集成有下面一些好处:

  • 易于定位错误
  • 更早在项目中取得系统级结果
  • 对项目结果更好的监控
  • 能在更短的开发计划中建造出整个系统

而增量集成有下面一些常见策略:

  • 自顶向下集成:先集成系统设计,再集成具体实现;优点是能更早有整个系统的大局观,缺点是在前期会加入很多底层的mock代码,且将调试过程推迟到项目后期
  • 自底向上集成:和自顶向下相反,优点是很容易定位错误,缺点是丧失全局的认识,系统设计的问题在后期修改成本较高
  • 三明治集成:先集成顶部的高层业务对象和底部的基础工具类,再集成中间层代码,整合了自顶向下和自底向上
  • 风险导向的集成:鉴别不同类的风险级别,先集成风险高的
  • 功能导向的集成:一次集成一组功能,它基本上不需要脚手架,且每次集成都能增强系统的功能性,且和面向对象设计比较好协同工作
  • T型集成:在集成高层对象后,先选中某个特定功能块,完成一次所有类的集成,这样可以作为MVP演练整个系统

结合冒烟测试的“每日构建”(daily build)是软件集成的一种最佳实践。它能让产品每天都有进步,且让项目保持一个固定的脉搏。做好daily build,有下面一些建议

  • 不放过失败的build,保证每次build都能通过冒烟测试
  • 每天进行冒烟测试
  • 冒烟测试需要和代码一样“与时俱进”
  • 让daily build和冒烟测试自动化
  • 要求开发人员构建前进行冒烟测试
  • 将修订保持合适的合并节奏,不要太密,也不要太疏
  • 在早上发布build,给潜在问题留下修复时间
  • 顶住需求压力,保证daily build和冒烟测试

在daily build的基础上,可以很轻易地做到1日多次的持续集成

编程工具

工欲善其事,必先利其器

现代化的编程环境下,有很多可以采用的编程工具:

  • 设计工具
  • 源代码工具
    • IDE
    • 文本替换工具
    • diff工具
    • merge工具
    • 源代码美化器
    • 接口文档生成
    • 代码模板/代码生成
    • 命令行
    • 代码质量分析
      • linter
      • metrics报告
    • 重构代码
      • 重构器
      • 代码翻译器
    • 版本控制工具
    • 数据字典
  • 可执行码工具
    • 目标码生成
      • 编译器、链接器
      • build工具,如make、ant
    • 程序库/第三方库
    • 代码生成向导
    • 安装指引
  • 调试
  • 测试
  • 代码调整
    • 性能剖测
    • 汇编和反汇编

在Unix这样的工具导向环境下就更容易孕育编程工具,如grep、diff、sort、make、tar、line、sed、awk、vi等。几乎所有的大型组织都有自己的内部工具和支持团队,不少比市面上的还要优秀。针对特定项目,有时候也会开发特定的项目工具,如航天、保险、医疗等。对于个人开发,也可以使用脚本这种自动执行重复性杂务的工具。

最后要澄清一个事实,编程工具并不能消灭人在编程里的核心地位,只是不断重塑(reshape)编程的含义。连接到其他软硬件的复杂接口,规章制度、业务规则这些计算机编程之外的复杂之源还是要人来应对。而被用来填补真实世界和解决问题的计算机之间鸿沟的人,被称作程序员。

软件工艺

编程是硬件与艺术的融合体,软件工艺是编程美学的一种体现。

布局与风格

编排出色的代码会带来视觉上和思维上的愉悦。

  • 基本原则
    • 好的布局可以凸显程序的逻辑结构,也更符合人类直觉
    • 傻子都会写计算机理解的代码,而优秀程序员写的是人能看懂的代码
    • 高手的机型并非天生优于新手,而是高手具备某种知识结构,这种结构有助于高手记住特定类型的信息;因此当信息符合这些结构时,就可以被轻易的理解
    • 布局非信仰,要保持头脑开放,接受已被证实更好的方法
  • 布局技术
    • 空白
      • 分组
      • 空行
      • 缩进
    • 括号
  • 布局风格:同一层级的语句缩进相同
    • 纯块结构
    • 模仿块结构
    • 花括号指定边界
    • 行尾布局(不推荐)
  • 控制结构布局
    • 段落间的空行
    • 复杂的表达式拆分条件到多行
    • 不用goto
  • 单行语句布局
    • 控制长度
    • 使用空格
    • 后续行缩进统一
    • 后续行结尾统一
    • 一行一条语句
      • 减少复杂度
      • 读代码仅需自上而下
      • 不要在单行中多个操作
    • 一行一个声明
  • 注释风格
    • 缩进和代码一致
    • 用空行和代码隔开
  • 子程序布局
    • 空行分段
    • 参数按标准缩进
  • 类布局
    • 头部注释 -> 构造函数/析构函数 -> public子程序 -> protected子程序 -> private子程序和成员
    • 文件布局
      • 一个文件一个类
      • 文件命名和类有关
      • 在文件中清晰分隔各子程序

自说明代码

本节专注于文档的特殊补充形式,即“注释”。

  • 在代码中起主要作用的并非注释,而是好的编程风格
  • 注释的哲学
    • 注释能提供更高层级的抽象
    • 重复注释根本没用
    • 注释写的不合适只会起反作用
  • 注释类别:在代码完工后,只允许出现后三种
    • 重复代码(Bad case)
    • 解释代码:当代码过于复杂到需要解释时,最好是改进代码,而不是添加注释
    • 代码标记,如TODOFIXME
    • 概述代码
    • 意图说明
    • 传达代码以外的信息,如版权声明、保密要求
  • 高效注释
    • 用伪代码法减少注释时间
    • 将注释如何到开发风格中
  • 注释技术
    • 注释单行
      • 去掉无关注是
      • 减少行尾注释
      • 行尾注释只用于数据声明、维护标记、标记行尾等场景
    • 注释代码段
      • 应表达why而非how
      • 代码本身应尽力组做好说明
      • 注明非常规用法
      • 错误或语言环境独特点要加注释
    • 注释数据声明
      • 数值单位
      • 允许范围
      • 输入限制
      • 全局数据
    • 注释控制结构
      • 循环结束的行尾注释是代码太复杂的征兆
    • 注释子程序
      • 注释要靠近说明的代码
      • 用简短的话进行说明
      • 注释声明参数
      • 可以使用Javadoc这种工具
      • 说明子程序的全局作用
    • 注释类、文件、程序

个人性格

软件工程是纯粹的脑力劳动。软件工程师研究工具和原材料的本质时,实际上是在研究人的智力、性格这种无形的东西。

  • 编程工作本质上是项难以监督的工作,你也需要对自己负责
  • 聪明和谦虚
  • 求知欲
    • 形成自我意识
    • 实验
    • 学习成功项目
    • 阅读文档
    • 和同行交流
  • 不屈不挠的诚实感
  • 交流和合作:编程首先是与人交流,然后才是和计算机交流
  • 创造力和纪律
  • 懒惰:避免“实在懒”,追求“开明懒”和“一劳永逸的懒”
  • 可能不那么明显的性格
    • 坚持:要时不时抬头开清方向
    • 经验:不同于其他行业,软件开发行业的经验比书本知识价值要小,基础知识变化很快,不存在越老越吃香的情况。不持续学习跟上潮流,仅靠经验吃饭,会被逐渐淘汰。
  • 习惯
    • 好习惯很重要
    • 不要用“没有习惯”替代“坏习惯”

软件工艺探讨的话题

《代码大全》全书都着重于软件构建的细节,本章从抽象的关注点出发,看看哪些方面会影响软件的工艺。

  • 软件开发的核心是致力于降低复杂度,管理复杂度是软件的核心使命,之前各章节提过了很多具体办法。各种形式的抽象都是管理复杂度的强大工具。
    • 划分子系统
    • 仔细定义类接口
    • 保持接口抽象性
    • 避免全局变量
    • 避免深层次继承
    • 避免深度嵌套和循环
    • 不用goto
    • 子程序短小精悍
    • 使用清晰明了的变量名
    • 使用规范和约定减少理解负担
  • 软件开发和其过程密不可分,在多程序员参与的项目里,组织性的重要性超过了个人技能
    • 坏的过程只会损耗脑力,好的过程则可以开发脑力到极限
  • 首先为人写程序,然后才是机器,强调代码可读性,便于与同行沟通
  • 深入一门语言去编程,不浮于表面
    • 杰出的程序员会考虑他们要干什么,然后才是怎么用手头的工具实现目标
  • 借助规范集中注意力
  • 基于问题域编程
    • 将程序划分为不同层级的抽象
      • 第0层:操作系统的操作和机器指令
      • 第1层:编程语言结构和工具
      • 第2层:底层实现结构,如算法和数据结构
      • 第3层:低层问题域,这一层已经有问题域相关的操作原语可以使用
      • 第4层:高层问题域,你的非技术用户某种程度也应该可以看懂你的代码
  • 编程是科学和艺术融合的一门工程学科
  • 迭代在软件开发中是很正常的现象。软件设计是一个逐步精化的过程。
  • 将软件和信仰分离开
    • 不要盲目跟风
    • 保持折中态度
    • 权衡各种技术,再做决定
    • 基于实验,保持开放心态

–END–

0%