R語(yǔ)言為何令我忍無(wú)可忍

在電氣電子工程師學(xué)會(huì)(IEEE)發(fā)布的2016年編程語(yǔ)言排行榜中拼窥,R語(yǔ)言已經(jīng)沖到了第五名退敦,僅次于C折柠、Java芒炼、Python 和 C++。R語(yǔ)言的流行滨彻,大致是與數(shù)據(jù)科學(xué)的興起有關(guān)藕届。用R來(lái)分析數(shù)據(jù),成了一件很時(shí)髦的事情亭饵。

我在R語(yǔ)言及其前身S-Plus里面摸爬滾打了多年休偶,卻一點(diǎn)也不喜歡,因?yàn)镽語(yǔ)言有許多令我忍無(wú)可忍的腦殘?jiān)O(shè)計(jì)辜羊。在本文里踏兜,我舉兩個(gè)例子,展示冰山一角八秃。

例1:自作聰明的序列產(chǎn)生器

不少編程語(yǔ)言里碱妆,都有序列產(chǎn)生器,例如:

  1. Haskell 中 [3..7] 的計(jì)算結(jié)果是 [3,4,5,6,7]昔驱。而[7..3]的結(jié)果是空表[]疹尾。
  2. Matlab 與 Haskell 的行為類似,3:7 的計(jì)算結(jié)果是 3 4 5 6 7舍悯。7:3的結(jié)果是空矩陣 Empty matrix: 1-by-0航棱。
  3. Python 中采取 Dijkstra 慣例睡雇,整數(shù)區(qū)間不包含上界本身萌衬。于是range(3,7) 的計(jì)算結(jié)果是 [3,4,5,6]。而range(7,3)的結(jié)果與前兩種語(yǔ)言類似它抱,是空表[]秕豫。

現(xiàn)在R登場(chǎng)了!

3:7

得到

3 4 5 6 7

這個(gè)還正常』煲疲現(xiàn)在祠墅,倒過(guò)來(lái)……

7:3

R竟然“聰明”地給出了一個(gè)倒序的向量!

7 6 5 4 3

在R里摸爬滾打了許多年歌径,我淺薄地認(rèn)為毁嗦,這種標(biāo)新立異其實(shí)是個(gè)大坑!

下面回铛,我們做一個(gè)程序狗准,列出 “從1、2茵肃、3腔长、4這四個(gè)數(shù)中,取出任意兩數(shù)” 的所有方案验残。為了描述清晰捞附,我先用 MATLAB 做出一個(gè)版本:

for i=1:4
    for j=(i+1):4
        fprintf('%d %d\n', i, j);
    end
end

計(jì)算結(jié)果是:

1 2
1 3
1 4
2 3
2 4
3 4

下面我們將MATLAB版本直譯成R語(yǔ)言:

for (i in 1:4) {
    for (j in (i+1):4)
        print (c(i, j))
}

我們希望得到類似MATLAB版本的輸出,然而您没,R的計(jì)算結(jié)果令人發(fā)指:

[1] 1 2
[1] 1 3
[1] 1 4
[1] 2 3
[1] 2 4
[1] 3 4
[1] 4 5
[1] 4 4

這個(gè)結(jié)果讓人一時(shí)摸不著頭腦鸟召。在上面的結(jié)果中,前面六行沒(méi)有問(wèn)題氨鹏,但第七行也太離譜了药版!根據(jù)正常的邏輯,j的最大值無(wú)論如何也就是4喻犁,為何會(huì)產(chǎn)生5呢槽片?這就是因?yàn)镽的序列產(chǎn)生器自作聰明!

這個(gè)問(wèn)題的重點(diǎn)是肢础,當(dāng)i=4的時(shí)候还栓,j的范圍到底是什么。包括MATLAB在內(nèi)的正常語(yǔ)言传轰,都會(huì)認(rèn)定此時(shí)j的范圍是空集剩盒,從5到4產(chǎn)生一個(gè)序列,下界比上界大慨蛙,當(dāng)然是空集咯辽聊!但是R偏偏不這么想,它說(shuō)期贫,下界比上界大跟匆,那么我們產(chǎn)生一個(gè)逆向的序列吧:5,4。于是通砍,就有了第七和第八行這兩對(duì)多余的答案玛臂。

這里烤蜕,一定有人說(shuō),博主SB迹冤,把 for (i in 1:4) 寫(xiě)成 for (i in 1:3) 避免下界比上界大的情況讽营,不就把這坑繞過(guò)去了嗎?我不想這么做泡徙,因?yàn)檎Z(yǔ)言是拿來(lái)用的橱鹏,不是拿來(lái)練習(xí)“繞坑”的!

例2:神奇的等號(hào)與箭頭

先做個(gè)變量 x 堪藐,賦值為0蚀瘸。然后,我們來(lái)玩一個(gè)簡(jiǎn)單的“比大小”的游戲庶橱,比較 x 和 -1 的大小贮勃。具體而言,做一個(gè)if表達(dá)式苏章,完成以下邏輯:

  • 如果 x 小于 -1寂嘉,取值為“躲”
  • 否則取值為“策”

稍有常識(shí)的觀眾都知道 0 比 -1 大,所以當(dāng)x=0時(shí)枫绅,這個(gè)if表達(dá)式的值應(yīng)該是“策”泉孩。那我們拭目以待:

x=0
if (x<-1) "躲" else "策"

在R里面執(zhí)行一下,結(jié)果是

"躲"

有一定經(jīng)驗(yàn)的讀者或許已經(jīng)發(fā)現(xiàn)問(wèn)題了并淋。我來(lái)班門(mén)弄斧寓搬,仔細(xì)拆解一下。在解釋這一行程序的時(shí)候县耽,R完成了一系列動(dòng)作:

  1. <-合在一起當(dāng)成賦值操作句喷。
  2. 把 x 賦值為 1。
  3. “賦值”這一操作給出一個(gè)值:1兔毙。
  4. 把數(shù)值1當(dāng)作“邏輯真”代入 if 表達(dá)式唾琼,條件滿足了:“躲”。

這里澎剥,一定有人說(shuō)锡溯,博主SB,專寫(xiě)有歧義的表達(dá)式哑姚,如果在 -1 之前加個(gè)空格:

if (x< -1) "躲" else "策"

不就把這坑繞過(guò)去了嗎祭饭?我還是不想這么做,因?yàn)檎Z(yǔ)言是拿來(lái)用的叙量,不是拿來(lái)練習(xí)“繞坑”的倡蝙!

關(guān)于這種設(shè)計(jì),我想說(shuō)幾句:

  1. “賦值”這個(gè)操作宛乃,作為一種動(dòng)作悠咱,還是沒(méi)有值比較好。在沒(méi)有上下文的情況下征炼,給“動(dòng)作”定一個(gè)值析既,是一件反自然、反人類的事情谆奥。比如眼坏,“傘哥把我打了一頓” 這個(gè)動(dòng)作的值是什么,是“爽”還是“疼”呢酸些?在以上的 if 表達(dá)式中宰译,x<-1 就有個(gè)值。根據(jù)R的文檔魄懂,x<-1 的值沿侈,與賦值后的 x 一樣,在這里是 1市栗,這種行為與C語(yǔ)言的賦值語(yǔ)句類似缀拭。之后,這個(gè)值1填帽,雖然是一個(gè)數(shù)蛛淋,卻被 if 拿去強(qiáng)行當(dāng)成“邏輯真”,這又與C語(yǔ)言的邏輯機(jī)制類似篡腌。R沒(méi)學(xué)到C語(yǔ)言的高效率褐荷,卻繼承了一大堆糟粕。

  2. 如果R分不清我是要“賦值”嘹悼,還是要“比大小”叛甫,至少應(yīng)該給我個(gè)提示,讓我自行確認(rèn)吧杨伙。無(wú)論是什么語(yǔ)言合溺,在 if 表達(dá)式的條件里做“賦值”是很罕見(jiàn)的事情。在這個(gè)例子中缀台,x<-1 就是 if 表達(dá)式的條件棠赛,但R解釋器對(duì)這種罕見(jiàn)的情況熟視無(wú)睹。相比之下膛腐,C語(yǔ)言雖有“賦值語(yǔ)句帶值”的坑睛约,而C的編譯器卻往往有不錯(cuò)的檢查機(jī)制。許多學(xué)過(guò)C語(yǔ)言的朋友哲身,都曾不小心寫(xiě)出過(guò)類似

if (x = 1) {
   ...
} else {
   ...
}

的結(jié)構(gòu)辩涝。在這種情況下,一個(gè)負(fù)責(zé)任的C編譯器會(huì)警告說(shuō)lvalue異常勘天≌看到這種警告捉邢,合格的C程序員都會(huì)再次確認(rèn),到底是要寫(xiě) x = 1 還是 x == 1商膊。然而伏伐,C編譯器的警告功能,R解釋器卻沒(méi)學(xué)到:完全無(wú)視上文的 if晕拆,不報(bào)錯(cuò)藐翎,不給出任何提示,一意孤行实幕。在這種情況下吝镣,再狡猾的程序員,也躲不過(guò)好 bug 袄ケ印末贾!

  1. 以上的例子中,程序員看到值不對(duì)整吆,可以迅速排錯(cuò)未舟。然而,錯(cuò)誤的程序能有時(shí)能產(chǎn)生正確的結(jié)果掂为。例如裕膀,把剛才的需求調(diào)整一下:現(xiàn)在,如果 x 小于 -1勇哗,取值為“策”昼扛,否則取值為“躲”。取 x=-2欲诺,有 bug 的程序如下:
x=-2
if (x<-1) "策" else "躲"

這樣一來(lái)抄谐,只要一運(yùn)行,就得到

"策"

這個(gè)結(jié)果是符合需求的扰法,然而程序完全錯(cuò)誤蛹含!表達(dá)式給出的這個(gè)“策”,不是因?yàn)椤皒 小于 -1”塞颁,而是因?yàn)椤皒被賦值成1浦箱,且1被當(dāng)成邏輯真”。這樣一來(lái)祠锣,錯(cuò)誤的程序弄出了看起來(lái)正確的結(jié)果酷窥,這使得調(diào)試非常困難。如果成千上萬(wàn)行程序中藏了個(gè)類似的錯(cuò)誤伴网,一時(shí)通過(guò)了測(cè)試蓬推,直到項(xiàng)目交付之后才爆出問(wèn)題,就更麻煩了澡腾。

結(jié)語(yǔ)

編程語(yǔ)言沸伏,不是誰(shuí)都能設(shè)計(jì)的糕珊。稍有不慎,就會(huì)制造許多“坑”毅糟,甚至還會(huì)把某些“坑”當(dāng)做“特性”红选。R這種東西,拿來(lái)當(dāng)繪圖工具或是計(jì)算器倒是可行留特,卻也不是必須的纠脾。若要拿它做重要的事情玛瘸,最好再三考慮蜕青。寫(xiě)到這里,我已將R卸載了糊渊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末右核,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子渺绒,更是在濱河造成了極大的恐慌贺喝,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宗兼,死亡現(xiàn)場(chǎng)離奇詭異躏鱼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)殷绍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)染苛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人主到,你說(shuō)我怎么就攤上這事茶行。” “怎么了登钥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵畔师,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我牧牢,道長(zhǎng)看锉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任塔鳍,我火速辦了婚禮度陆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘献幔。我一直安慰自己懂傀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布蜡感。 她就那樣靜靜地躺著蹬蚁,像睡著了一般恃泪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上犀斋,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天贝乎,我揣著相機(jī)與錄音,去河邊找鬼叽粹。 笑死览效,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的虫几。 我是一名探鬼主播锤灿,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼辆脸!你這毒婦竟也來(lái)了但校?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤啡氢,失蹤者是張志新(化名)和其女友劉穎状囱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體倘是,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亭枷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搀崭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叨粘。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖门坷,靈堂內(nèi)的尸體忽然破棺而出宣鄙,到底是詐尸還是另有隱情,我是刑警寧澤默蚌,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布冻晤,位于F島的核電站,受9級(jí)特大地震影響绸吸,放射性物質(zhì)發(fā)生泄漏鼻弧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一锦茁、第九天 我趴在偏房一處隱蔽的房頂上張望攘轩。 院中可真熱鬧,春花似錦码俩、人聲如沸度帮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)笨篷。三九已至瞳秽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間率翅,已是汗流浹背练俐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冕臭,地道東北人腺晾。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辜贵,于是被迫代替她去往敵國(guó)和親悯蝉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理念颈,服務(wù)發(fā)現(xiàn)泉粉,斷路器连霉,智...
    卡卡羅2017閱讀 134,626評(píng)論 18 139
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程榴芳,因...
    小菜c閱讀 6,362評(píng)論 0 17
  • 我輕喚一聲月 月兒云里藏 撥云見(jiàn)圓月 圓月咯咯笑 笑成你笑意如花的臉龐 我輕喚一聲月 把月光寫(xiě)成一封無(wú)字的...
    夏小時(shí)閱讀 209評(píng)論 0 1
  • 小時(shí)候,幸福是一顆糖跺撼;長(zhǎng)大后窟感,幸福是一次玩耍;現(xiàn)在歉井,幸福是一種感覺(jué)柿祈。閉上眼,聽(tīng)風(fēng)拂過(guò)耳畔哩至,才恍然大悟躏嚎,幸福原來(lái)那么...
    執(zhí)筆溢情閱讀 525評(píng)論 0 3