【遊戲】線上遊戲的轉蛋機制設計與倫理議題

轉蛋機制是免費制線上遊戲(free to play,以下簡稱F2P)中常見的遊戲機制。一言以敝之,這樣的機制消耗了一定數量的遊戲貨幣,並以機率的形式釋放遊戲獎勵。這些遊戲獎勵可能是價值不菲的,例如一張珍貴的角色卡片、關鍵的轉職材料或是罕見的裝備;但更多的是一些不怎麼樣的物品,例如一些普通的角色、雜物或貨幣。

在筆者的工作與遊戲經驗裡,對於F2P線上遊戲而言,轉蛋無疑是最有效能產生營收(monetization)的遊戲機制。儘管我們無法得到任何一款公司提供的確切產品統計,筆者也無法透露自己參與過的專案數據;我們仍然可以透過觀看玩家的社群討論或已公開的抽卡影片與直播,來得到這樣的結論。我想,對於任何遊戲從業人員而言,我想也沒有人會訝異:有不少玩家願意花三五十萬台幣在一款遊戲的轉蛋機上。

在這篇文章中,筆者將討論一些簡單的轉蛋設計方法,以及其所衍發相關的商業倫理議題

簡單純機率

在遊戲製作的實務裡,轉蛋機制往往遠較想像中複雜。從外觀上來看,轉蛋機通常有著一個符合遊戲背景故事的外表(可能是一台轉蛋機、一個召喚怪物的魔法陣、或是打開一個神秘的寶箱)。不論這個機制被包裝得如何,它們通常讓玩家聯想到生活中常見的籤筒抽籤:一個桶子裡有各種的籤,少數代表中獎,但大部分都代表銘謝惠顧或一些雜物。在抽籤的過程裡,每一次抽籤都是獨立事件

轉蛋示意圖 圖片:〈怪物彈珠〉抽取卡片的畫面。轉蛋機制會被包裝成符合遊戲故事背景的樣子。

這樣單純的機制筆者稱之為簡單純機率,以下的機率表即可作為示範。它非常地直觀且好懂:每次抽取都是獨立且公平的;玩家每一抽有5%的機率抽中S卡;若每一抽的價格是100台幣,則抽中一張S卡的平均價格(期望值)會是2000台幣。

簡單純機率表

這樣的簡單純機率看似正常,但其實大有問題。舉例來說,這意味著玩家付出4000台幣(也就是兩倍於期望值的價格),卻仍抽不到S卡的機率其實是13%——如果這發生在遊戲初期,這代表著你有13%具有高度價值的玩家,將承受幾乎毀滅性的遊戲體驗:不只花了大錢卻空手而歸,還必須眼睜睜看著為數眾多的免費試玩玩家在第一抽就抽中S卡(這樣的機率是5%,且第一抽常常是免費的)。

以統計學來說,這是一個典型的二項分布(Binomial Distribution)。下圖顯示了一個p=5%n=40的圖例,當n=40p(x=0)=12.9%,亦即玩家即便願意花4000元,仍然有很高的機率抽不到任何一張S卡(但是有9%的人可以抽到四張)。

二項分布

也許你會這樣想:呃⋯⋯至少這很公平,而且這不過是個遊戲而已——直到你的客服信箱被憤怒的玩家轟炸;臉書專頁與應用程式頁面都被心碎的付費玩家塞滿一星評價與崩潰留言為止。玩家出於對遊戲的愛而付費,當他們承受了毀滅性的遊戲體驗,這樣的愛很容易會轉成熾烈的怨恨。

此外,在台灣與中國,許多遊戲公司往往與最願意花錢的大戶保持私下的、程度不一聯繫,並提供諸如到府收款、專案客服等特殊服務;對於一般在社群網站上活躍的、具有影響力的、願意花個幾千元的玩家,亦有一定程度的交流。當這樣令人不滿的遊戲情境發生時,遊戲公司往往會為了產品形象著想,而願意提供私下的補償。然而,補償的標準要如何設定?寶物的發放要如何控管?又如何避免玩家口耳相傳變成會吵的孩子有糖吃?這對於遊戲經營團隊都會是嚴重的困擾。

更重要的是,有些玩家心碎了,往往也就無聲地離開了。

簡單純機率有什麼問題?

在討論簡單純機率所衍生的的問題前,我們必須先討論何謂遊戲的強度/獎勵。為了方便閱讀過程中的想像,讀者可以將這篇文章提到的「遊戲」設定為幾款熱門的F2P作品,諸如〈神魔之塔〉、〈刀塔傳奇〉或〈皇室戰爭〉。

在一款F2P的的遊戲產品裡,遊戲設計師往往為玩家配置了各種獎勵——又或著說是所謂的強度,來鼓勵玩家的各種遊戲行為。這些行為包含了:

活躍強度/獎勵

意指玩家透過持續地玩遊戲,所能得到的強度與獎勵。你可以把它想像成遊戲時間強度/獎勵的函數。在大部分的F2P遊戲裡,這樣的函數常常是(也必須是)遞減的,但其遞減幅度、以及何時開始遞減,端看產品鎖定的族群而定。舉例來說,學生族群可能可以投注較長的總時數在遊戲裡(放學後有較長的空閒);但對於遊戲的頻率,卻往往不如上班族(上學不能常常用手機)。當產品鎖定的族群不同,活躍強度的函數設計亦會不同。

當時間函數遞減太快,玩家會容易感到無聊(想玩遊戲卻無法得到獎勵);當時間強度遞減太慢時,玩家則會感到強烈的作業感(獎勵捨不得不拿,但又沒那麼多時間一直玩)。

付費強度/獎勵

意指玩家透過付費,所能得到的強度與獎勵,也就是金錢強度/獎勵的函數。舉個極端的例子而言,〈英雄聯盟〉作為一款純電子競技類型的遊戲,其付費幾乎是完全與強度脫鉤的[1],也就是幾乎不存在pay to win的現象;而相反的,〈皇室戰爭〉則透過卡牌的搜集與升級機制,將強度與付費做了一定程度的掛鉤。

社交強度(獎勵)

意指玩家透過與其他玩家互動,所能得到的強度與獎勵。標準的例子例如農場遊戲常見的拜訪獎勵;〈Candy Crush〉系列贈送愛心的機制以及〈神魔之塔〉的公會任務。在長線的遊戲經營裡,社交機制往往是最後能否留下玩家、延長產品壽命的關鍵。

技術強度(獎勵):

意指玩家透過本身的遊戲技術,所能得到的強度與獎勵。這個概念也不難理解:〈英雄聯盟〉就是一個幾乎100%技術強度的遊戲,付費者並不會因為付費而變得更強;而〈皇室戰爭〉大概是付費與技術各佔50%;至於〈刀塔傳奇〉中的競技場裡,付費玩家幾乎可以獲得絕對的優勢。

掌握不同行為獎勵之間的均衡,是遊戲設計至關重要的議題。對於F2P遊戲而言,付費強度的設計又可以說是重中之重。

然而,簡單純機率以及其所連帶的期望值計算,卻無法負擔這樣精細的操控。舉例來說,當一個設計師期待一張S卡所帶來的強度約莫等值於2500~3500台幣之間時,簡單純機率的轉蛋機卻從來無法保證這樣精準的控制。因為,玩家可能會抽到很多張,也可能一張都抽不到。

總得有個辦法吧⋯⋯

儘管簡單純機率有這麼多的問題,轉蛋機制與機率仍然是F2P產品最有效的盈利機制之一。諷刺地是,如果我們直接出售一張S級卡片,並將之標價為三千台幣,玩家很有可能直接嘲笑製作團隊想錢想瘋了:這不過是個遊戲道具,以及一條卑微的資料庫記錄而已。人應該回到現實、多吃大餐、多看電影、多去旅行、買買喜歡的衣服、甚至是拿去吃O喝X,而不該沈溺在虛擬的世界裡⋯⋯

然而,當我們把一張卡片放進轉蛋機裡,並且將期望值設定為三千台幣時,這一切又瞬間變得饒富趣味且值得討論了起來。或許我們可以說:觀察、議論進而承擔風險,並取得風險帶來的期待與獎勵,本身就是遊戲最有趣(甚至是所謂令人成癮)的一部分。

對於轉蛋,必須要有比簡單純機率更好的設計才行。

事實上,你可以放入任何演算法在轉蛋機裡⋯⋯

是的,轉蛋機的背後不過是幾行程式碼[2]。任何演算法,只要能產生貌似隨機的結果,且具備不易被逆推的特色,都可以用來驅動轉蛋機。你討厭遊戲ID是5結尾的玩家,或是email裡有william一詞的人嗎?開間遊戲公司,做出一款好遊戲,你可以請數值企劃和工程師讓這些玩家抽卡抽到懷疑人生[3]⋯⋯

接下來筆者會舉出些常見但基本的轉蛋設計技巧,以及其所可能對應到的遊戲體驗,作為範例。

範例1:取出不放回

取出不放回是一個基礎統計常討論的模型。當應用在遊戲設計裡,它可以確保玩家至少在n抽裡獲得一個或數個以上的大獎,卻仍然保持了機率所帶來的有趣性。

以下圖為例,這台簡化的轉蛋機以20為一個循環,每次20抽裡會確保玩家得到一張S卡。相較於簡單純機率,超級倒霉與超級好運的極端狀況就消失了,對於付費與強度的關聯控制也變得更加精準,但仍然保有一些機率的樂趣。

取出不放回

當然,這樣的設計也並非全無缺點,例如某些具備經驗的玩家可以精準地抓出其中的規律,並公告在網路社群上,這可能使得玩家對於轉蛋失去期待感。不過,當母池容量變得巨大,且混合了更多隨機機制時,其實出獎規律仍然不是那麼容易抓到的。

在實務裡,這樣的機率配置可能是隱匿的。但設計師亦可採用部分揭露或是完全揭露的機制:前者會讓玩家意識到自己中特獎的機率逐漸提高[4];而後者則會直接在遊戲的使用者介面上,讓玩家理解到母池的概念。

範例二:條件對應

這如之前所提:在過分簡單的外表下,轉蛋機制的背後可以是任何型式的演算法。對於遊戲工程師而言,只要數值企劃能提供明確的演算過程,將之透過程式實踐通常不會是太困難的事。

條件對應亦是一個可使用的設計手法。其原理在於:在實踐機率的運算之前,先以其他指摽進行非機率的邏輯切換。

舉個簡單的例子,在下圖中,轉蛋機制會記錄玩家的總抽取次數,再以抽取次數進行除以三的餘數運算(%3),再依照餘數對應至不同的簡單純機率表格。在這個例子裡,玩家會不斷地依序抽取到卡牌、武器、貨幣。

次數條件對應

當然,這樣過分簡化的機制是很容易被玩家察覺的。然而,遊戲設計師可以將條件對應機制與其他的模式結合,例如前段所提到的取出不放回

下圖中我們示範了一個混合了條件對應+取出不放回+簡單純機率的轉蛋設計。在一個有十顆彩球的母池裡;每種顏色的彩球對應到了不同等級的獎勵。在這樣的抽獎循環裡,玩家每十次抽獎都會得到一張S卡/兩張普通卡/兩件裝備/五個遊戲貨幣包。至於玩家會拿到哪一張S卡,哪兩張普通卡呢,以及以什麼順序抽到各種獎勵呢——這部分或許就是設計師願意讓機率決定並作為樂趣的範圍了。

綜合應用

範例三:付費與強度之外的考量

轉蛋機制的機率設計往往也不只是為了坑錢這麼單純的考量。舉例來說,在卡牌遊戲裡,玩家手牌的組合往往是前期體驗展開社交與合作的重要關鍵——作為一個設計師,可能不會希望玩家在前期裡滿手都是坦克而沒有攻擊手;又或是過早取得了完全均衡的團隊,以至於失去和其他玩家互動、租借卡牌的需求。

因此,牌組的多元性也可以是設計師操作機率的考量之一。對於數值企劃而言,這其實是一種透過數學與統計創造體驗的技術。舉例來說,當設計師規劃了數組可能的策略牌組時,他可以要求工程師以條件對應的方式,將玩家依照用戶ID的尾數(玩家甚至可能不知道這個數字的存在),將轉蛋機對應至不同的抽卡路徑。這樣的做法,可以確保玩家獲得某種(鬆散但不是絕對的)牌組雛形,也能確保多元的遊戲話題與玩家之間的合作。

轉蛋機制的倫理爭議與規範的困難

行文至此,讀者或許已經可發現其爭議之處——轉蛋機制以一個過分簡單的外表、絕少的資訊揭露,來掩蓋了十分複雜的機率黑盒子。當它被用作主要的營收機制時,其實是可能存在類似賭博與詐欺的疑慮的。舉個例子來說:當玩家看到一個特殊的轉蛋活動,宣告「S卡機率加倍時」,其背後的機率設定「可以」是完全沒變的。除此之外,這樣的機率也可以透過程式,進行動態調整

這的確是具有爭議的。在玩家的社群討論板裡,我們也常常看到因為消費聽驗不佳的用戶們,在討論著如何成功地與Google與Apple的客服申請退款。

這很有可能早已造成了兩大平台的困擾。儘管它們都曾試圖採取規範行動——主要著重在公開內容一項,但截至2019年中,這樣的規範似乎尚未產生具體的影響力。其關鍵在於,不論是公開內容甚至是機率,都仍然侷限於簡單純機率的想像裡,對於更多複雜的設計方法,諸如取出不放回條件對應以及各種機率設計的交互應用,還是無以規範。更重要的是:兩大平台既不可能要求廠商公開轉蛋機制的原始碼[5];亦無以透過外在的實驗去進行逆向檢核,以至於即便廠商公布了資訊,玩家也無從得知其是否為真。

公布機率 圖片:〈皇室戰爭〉中揭露寶箱內容的介面。筆者認為,複雜的機率很難用介面顯示;顯示了也不一定包含完整的細節;完整了也不一定是真的;就算是真的,還是可以用程式進行動態調整。

總結地說,遊戲設計的複雜性更加深了規範的困難。首先,正如文章中所言:轉蛋機制擁有多元的外皮,要執行有效的規範,更不容易。其次;部分的轉蛋亦包含了付費以外,諸如遊戲體驗與平衡類的考量;最後,在當代的遊戲設計裡,轉蛋使用的貨幣亦不全然來自於付費——以經典F2P遊戲〈神魔之塔〉為例,其抽卡用的貨幣可能是免費發送(諸如活躍獎勵與過關獎勵),亦可能是來自於付費購買的。如果以消費透明為由,要求廠商通盤公布可能屬於商業知識的機率配置,亦未必公允。

最後的最後,當遊戲營運商透過Google、Steam、Apple進行全球發行已成常態,區域政府的法規是否能有效規範並公平地規範每一個產品,亦是一大疑慮。舉例來說,如果台灣自行通過了較嚴格消費者保護法律,執法者卻只能針對本地的遊戲研發者執法,卻無力規範主流的跨國遊戲。這無疑讓競爭變得更不公平。

2019年的美國立法嘗試:兒童遊戲成癮防制法

2019年上半年,美國參議員Josh Hawley提出了名為The Protecting Children from Abusive Games Act的法案草案(暫譯:兒童遊戲成癮保護法),亦引起了遊戲開發者一定程度的重視。參議員Hawley主張:「以兒童為目標客群的遊戲,不該被允許從成癮行為裡盈利。當兒童玩著以成人為目標客群設計的遊戲時,他們亦應該被隔絕於成癮性的小額付費體驗之外。那些刻意從兒童身上剝削營收的遊戲開發者,必須面臨法律的懲處」

在具體的案例上,他標示出了兩大最被濫用的設計模式,其一是Pay to win[6];其二是Loot Box(也就是我們所謂的轉蛋機制)。在申論中,參議員Hawley主張:「Loot Box機制廣泛被採用於免費與付費的遊戲裡。這樣的機制提供了隨機的付費獎品,並結合了pay to win令人黏著的特性;以及沿襲自賭博的成癮行為」[7]

這是一個很有趣的提案,也是F2P的商業模式再次承受了來自法律規範的挑戰。礙於篇幅與精力,本篇文章在此點到為止,有興趣的讀者可自行追蹤相關報導與後續。

倫理議題

更廣遠地看,這世界上有著販賣有害健康食物的餐廳、政府做莊家的彩券與運動彩券、特許的民營賭場、配備高級音響但安全設備不足的汽車、文案曖昧但幾乎沒有實質效用的減肥藥與保健食品⋯⋯遊戲轉蛋究竟應該被做到何種程度的規範?我想並不容易回答。而遊戲作為常常被主流價值污名化與缺乏瞭解的標的,當又再次變成法律規範的焦點時,亦不難想像開發者心中是會有所不平的(你看過彩卷或刮刮樂公告中獎率嗎?什麼該被歸類為所謂「面向兒童」設計的遊戲?薯條和冰淇淋算是面向兒童設計的垃圾食物嗎?那迪士尼樂園裡到處陳列的超可愛Shellie May熊熊呢?)。

這題並不好回答,筆者也沒有打算在本篇文章中回答[8]。可以確定的是,作為一個開發者,亦不該一味迷信夠坑=賺錢=成功的教條。正如文章中所言,數值企劃是一門透過數學與統計創造體驗的技術,在過去的五年十年內,更好、副作用更低、更有回饋感與創意的盈利模式持續被優秀的開發者創造出來。團隊除了適時地討論、確認自己內部的倫理自律標準外,亦應持續重視、精進設計的技術,才能做出好的作品。


註解

[1] 英雄聯盟的強度仍然與付費有著些許微妙的掛鉤。例如付費玩家可以擁有更深的角色池以及更多的天賦頁面,可以針對對手做出更好的反制配置。

[2] 嚴格來說,應該是好幾十行百行和一堆資料庫配置。

[3] 但通常是你自己在遊戲研發的過程中會比較懷疑人生⋯⋯

[4] 例如,提供一個諸如「運氣指數」的指標,然後隨著母池的清空,逐漸顯示為普通/略高/極高。當玩家知道這些機率是由自己過去的消費所堆疊起來時,也常常更難以放棄。

[5] 就算真的公開原始碼,也不能確保伺服器上運作的版本與數值是公開的版本。

[6] 熟悉遊戲產業發展歷史的玩家與開發者應該都可以想像,完全禁止pay to win,也就是所謂付費與強度的連結,是一件多麽荒謬的事。

[7] 本段引言取材自網路。美國參議院網站,(May 8, 2019), Senator Hawley to Introduce Legislation Banning Manipulative Video Game Features Aimed at Children. 擷取自 https://www.hawley.senate.gov/senator-hawley-introduce-legislation-banning-manipulative-video-game-features-aimed-children

[8] 事實上是,社會裡的立法與規範形成的過程,往往也不只是透過倫理與應然的辯論而產生的。對於實力尚未充足的開發者而言,花太多時間討論倫理不如靜觀其變,打好研發技術才是正途。

【Web】徹底理解同源政策(Same Origin Policy)

對於網頁學習者來說,同源政策(Same Origin Policy)可能是一個很重要的困惑點。

當我們終於搞懂了client與server;理解了HTTP request/response;摸熟了AJAX;想出了一個絕佳的專案點子⋯⋯但不知為何,API的資料就是讀不出來。

於是我們開始研究關鍵字「同源政策」並持續Google,每次都好像又多懂了一點,但又不是那麼肯定——特別是在部署至production環境時,只要網域轉換,似乎就會發生不可預期的效果。

讓我們試著一次把它搞懂吧。

Meme of AJAX 圖片:Ajax迷因。Same Origin Policy是件麻煩的事(出處:reddit)

瀏覽器/使用者代理人/安全性

要理解同源政策,最核心的重點就是理解:

同源政策是由「誰」來執行?以及「為什麼」?

是HTTP(S)的嗎?還是JavaScript?是瀏覽器嗎?是網站框架嗎?還是TCP/IP?

如果能將以下重點銘記在心,我想在閱讀文件時就會感到豁然開朗:

同源政策,Same Origin Policy,是「瀏覽器」作為使用者代理人,為了「安全性考量」,所設定的規範。

什麼是使用者代理人?

請試著想像如下情境:我們拿著雙證件到銀行開了戶,也存了錢進去。行員告知:如果需要匯款或是其他操作,可以透過帳號+密碼登入銀行網站,在瀏覽器中進行——這樣就不用每次都跑銀行囉。

在現實世界裡,與我們互動的是銀行行員,我們可以透過證件與存摺存取帳戶;但在網路銀行中,與我們互動的對象成了Web server,而驗證身份的方式則變成帳號+密碼。

當我們請銀行行員協助時,使用的是人類的自然語言。但在網路銀行中,我們與web server是透過使用者介面/通訊協定/網路訊號來進行溝通的。

但作為一個人類,我們無法用大腦解析這一切。

於是我們需要一個代理人(user agent)——也就是瀏覽器,協助我們與web server進行溝通。這包含了渲染UI、設定Cookie、執行TCP三向交握、通訊加密、並將我們的表單解析成Http Request後送出⋯⋯等。

使用者的「安全性」是瀏覽器的職責

在操作的過程裡,瀏覽器有義務保護使用者的資料——尤其是涉及身份驗證的部分。

我們會透過瀏覽器登入各種帳號(例如網路銀行、臉書、Gmail、以及各式各樣來路不明的網站)。當成功使用帳號密碼登入時,這些服務的Web server會透過HTTP header, 提供一段auth token並要求瀏覽器存在cookie裡。在auth token的有效期間內,我們的http request都會自動帶上這段cookie,以此為憑,我們因此可以對服務進行各種操作,例如匯款、貼文、按讚、回覆email⋯⋯等。

但問題來了,如果有一個惡意的盜版漫畫網站evil.com,當我們在訪問時,透過頁面中JS以及AJAX,偷偷在瀏覽器中執行這樣的行為呢:

「請把銀行bank.com的cookie資料(包含auth token)讀取出來;透過AJAX+POST;回傳到https://evil.com/sniff

當然,作為一個使用者代理人,瀏覽器會拒絕如是請求。

拒絕的原則是什麼呢?那就是同源政策最重要的一環:「非同源的資料讀取是不被允許的」。既然evil.com不能讀取bank.com設定的cookie,那當然也無法將之回傳。

火狐姬

同源(Same-origin)的定義

如果能充分了解以上原則。剩下的,就只是技術細節的問題。

我們可以透過火狐家的MDN文件理解同源政策的規範。一言以敝之,我們可以把origin視做一個three-tuple(scheme, host, port),也就是(通訊協定, 網域, 埠號)的組合。兩個document的origin只要任一欄位不相同,就會被瀏覽器視作不同源[1]。

舉例如下:

此外,同源政策是以document為單位的。舉例來說:假設store.mystie.com的document透過<script>標籤取得了來自cdn.google.com的JS腳本,則該腳本的origin仍是由blog.mystie.com來定義。

在實務中,我們最常遇到的origin變數往往是網域。為了方便起見,以下討論會將網域等同於origin使用,但請注意origin包含的不只有網域而已。

SOP show domain 圖片:我們可以透過document.domain來取得文件所屬的網域。

跨源存取細節

討論到這裡,可能我們已經想到一堆例外了⋯⋯

  • 我的網站會透過CDN導入jQuery函式庫和Bootstrap的CSS,為什麼這不受限制?
  • 我的網站會使用<img>讀取外站圖片,為什麼這不受限制?

事實上,同源政策其實有許多細節,甚至各家瀏覽器的實作都會有些微不同。請注意,當我們在討論這個問題時,我們的角色已經轉變成服務的提供者與Web Server了。在研究同源政策時,釐清主詞(使用者/瀏覽器/網站後端)以及行為(request/response)是最重要的起手。

我們可以透過MDN的文件理解不同的跨源行為

跨源寫入(write)原則上允許

可以在A網域的document中提交請求給B網站;也可以透過超連結、轉址等方式連結至B網站。

跨源嵌入(embed)原則上允許

可以使用<img> <video> <iframe>標籤嵌入來自其他網域的資源。

可以使用<link rel="stylesheet" href="…">套用來自其他網域的CSS。

可以使用<scrip src="">標籤嵌入來自其他網域的腳本[3]

跨源讀取(read)原則上禁止

A網域的document不能讀取B網域的本地端儲存資料(例如Cookie)與回應。

值得注意的是:我們可以在A網域的document中,使用JS發送AJAX Request給B(跨源寫入允許);但就算B有回應;文件也無法讀取response(跨源讀取禁止)。例如這樣的jQuery操作$.get("https://www.google.com ")在所有非Google的document中,都會回報disallows reading的錯誤。

那第三方的Client端API是怎麼實作的?

如果跨源讀取是被禁止的,作為一個開發者(假設是交友網站),如果我們希望有一個第三方API服務(例如臉書),可以讓我們透過瀏覽器+AJAX取得資料,這其中的實作原理是什麼呢?

實情是:臉書作為第三方服務的提供者(務必注意主詞!),在回應請求時,是可以透過HTTP header中的Access-Control-Allow-Origin欄位,請瀏覽器特別通融。

舉例來說,當我們的交友網站向臉書申請API帳號時,臉書會提供一組token並詢問我方網域(假設是https://friend.com)。當我們使用這組token請求資料時,臉書就會在response中設定Access-Control-Allow-Origin=https://friend.com,翻成白話就是:

雖然 friend.com 與我不同源,但請准許他讀取這則HTTP response。

當收到這樣的回應時,瀏覽器就會允許friend.com之下的document讀取來自facebook API的回應囉[4]。

如果API server將Access-Control-Allow-Origin設定為*時,代表任何origin都可以讀取回覆,不受同源政策限制。

當然,回到一開始銀行的例子。基於安全性考量(只賦予必要的最小權限),當然就沒有必要設定這個欄位了。

client_app 圖片:使用client端API時,設定網域通常是必要的

那麼⋯⋯我們就這樣高枕無憂了嗎?

與同源政策常常一起討論,且觀念容易糾纏在一起的另一個主題,是所謂的跨域請求偽造攻擊(Cross Site Request Forgery)。這兩者彼此概念交錯,但其實同源政策並不能完全防止跨域請求偽造攻擊——不過這也是另一個主題了,我們就有空再說吧。

目前為止,希望我們已經清楚解釋了同源政策的大概念。


註解

[1] 在RFC-6454原文中的host對應的中文應該是「主機名稱」。但本篇文章主要聚焦於瀏覽器的一般用例(而非各種形式的user agent),因此使用「網域」一詞雖然不完全精準,但為了可讀性仍然選用。

[2] cookie的讀取權限可以下放給子網域。細節請見RFC-6256中的Domain Matching章節。

[3] 請注意這包含了「伺服器端後果自負」的前提。作為後端服務的開發者,在document中嵌入來自第三方(諸如Google CDN)的JavaScript意即「允許第三方腳本以我的origin身份進行操作」——這是另一個網頁安全性的大主題,有興趣的讀者可以搜尋Cross Site Scripting(XSS)。

[4] 請注意這只是允許讀取response,並不包含允許friend.com讀取facebook.com的cookie的意味。

[ref] IETF | RFC-6265 HTTP State Management Mechanism

[ref] IETF | RFC-6454 The Web Origin Concept

[ref] LiveOverflow | CSRF Introduction and what is the Same-Origin Policy? Concept

[ref] MDN | Same Origin Policy

[ref] stackoverflow | Does including all these 3rd party javascript files impose a security risk?

【香檳】人何寥落花何多

2020 年春天,COVID-19 在美國大爆發。香檳校園也早早宣布了暫停實體授課與學術活動。

大學城一但停了課,那肯定是百般寂寥的。而此次尤甚,因為連留守的國際學生們也多慌張離開了。

因為即將畢業與返國需要好好收拾,我算是走得遲的。那幾天又看日出完成了一個大作業。稍事休息,便至稍遠的住宅區把二手車賣給了另一位留學生。雖說是買賣,不知為何,心情更像是託付——車子一賣,離別便已是篤定的了。

因為眷戀;因為春天;也因為隔離得慌,我和朋友決定步行一個多小時回到校園。春天裡滿滿開花,鄉間優雅的小房子因為瘟疫,不見人煙。

從未想到一切會如此結束。但轉念思及人生,能預料的事終究不多,遂有釋懷感。

香檳的冬天好長;春天又好美好美。春逢大疫,人何寥落花何多。

【程式】使用JavaScript在中文與英文間墊上空格

中文文字工作者應該都聽過這個排版準則:

中英文交界處必須增加一個空格,避免視覺上過於壅擠[1]。

在部分的軟體或系統中,會自動墊上這樣的空間(包含你現在正看的這篇文章,以及諸如蘋果與Google的作業系統[2][3]);然而,並不是所有的讀寫介面都內建有這樣的功能。根據筆者實測,在2020年3月,Facebook的iPhone應用會自動墊上這個空間;但是在Web中則沒有——前者亦不確定是來自iPhone的作業系統,抑或是APP本身的功能。

中英文交錯的排版圖片:上圖中中英文沒有空間,顯得擁擠;下圖中則添加了半行空格。你覺得有差嗎⋯⋯可以確定的是,有人覺得有差。

所以⋯⋯我們要手動補上空白嗎

儘管這個空白是很常見的需求。然而,就筆者的觀點來看。在文本原始檔中「手動」添加空格,並不是一個理想的做法。其原因如下:

  • 非常容易疏漏。
  • 不同字體可能有不同適合的空間。
  • 在文本的原始碼中加入非必要的空格會讓管理複雜化,增加修訂、編輯與校對的負擔。
  • 以上,如果文本需要長期維護與修改,則花費的精力與效果不成比例。

讓程式來做吧!

但如果我們還是希望達成最舒適的排版,要怎麼做呢?

個人覺得最好的作法就是在閱讀的輸出端,利用程式自動墊上這個空間。

以本篇部落格為例,當訪客在讀取頁面時,筆者啟動了一段JavaScript,會將頁面中部分區塊的內容進行自動化處理。也因此,部落格中所有的文章,中英文間都會自動墊上一個半形空格。

該功能的實作如下:

function getPaddedCnEnString(htmlStr, padding) {
  // ASCII 33~126
  const EN_ALPHA = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
  const EN_SET = new Set(EN_ALPHA.split(""));
  const CN_MARKS = "。,、;:「」『』()—-?!——⋯⋯〈〉・《》_~";
  const CN_MARKS_SET = new Set(CN_MARKS.split(""));
  const CN_RANGE_MIN = 0x3400;
  const CN_RANGE_MAX = 0x9FFC;
  // utility function
  const isCn = (char) => ((char.charCodeAt(0) >= CN_RANGE_MIN && char.charCodeAt(0) <= CN_RANGE_MAX) ||
    CN_MARKS_SET.has(char));
  const isEn = (char) => EN_SET.has(char);
  // process
  let result = "";
  let thisIdx = 0;
  while (thisIdx <= htmlStr.length) {
    const thisChar = htmlStr.charAt(thisIdx);
    let nextIdx = thisIdx + 1;
    let nextChar = htmlStr.charAt(nextIdx);
    // copy contents in html tags (do no processing)
    if (thisChar === "<") {
      while (nextIdx <= htmlStr.length && htmlStr[nextIdx - 1] !== ">") {
        nextIdx++;
      }
      result += htmlStr.substring(thisIdx, nextIdx);
      thisIdx = nextIdx;
      continue
    } else if (nextChar === "<") {
      while (nextIdx <= htmlStr.length && htmlStr[nextIdx - 1] !== ">") {
        nextIdx++;
      }
    }
    result += htmlStr.substring(thisIdx, nextIdx);
    nextChar = htmlStr.charAt(nextIdx);
    // compare with next char and pad space if necessary
    if (isCn(thisChar) && isEn(nextChar)) { // 中文_abc
      result += padding;
    } else if (isEn(thisChar) && isCn(nextChar)) { // abc_中文
      result += padding;
    }
    // continue
    thisIdx = nextIdx;
  }
  return result;
}
  • 函式的輸入為:
    • htmlStr:字串,可包含HTML標籤(HTML標籤中的屬性不會被處理)
    • padding:你可以使用半形空白" "或是例如<span>標籤,再透過CSS來調整)
  • 函式使用Unicode判斷字元屬於中文還是英文,並將台灣官方的標準標點符號視做中文[4]。以此為判斷,自動在中英文間加上空間。

舉例來說,如果你的文章位於<div id="post">的標籤裡,那麼你可以這麼做[5]

$(document.onload).ready(function () {
  const element = $("#post");
  const newHTML = getPaddedCnEnString(element.html()," ");
  element.html(newHTML);
})

根據筆者簡單的測試,一份原始碼長5000字左右的markdown文章,以其HTML格式進行轉換,可以控制在5毫秒以內;且不妨礙原本內容的讀取;顯然不會造成太大的效能負擔。

以上,獻給排版的偏執狂們。


註解

[1] 儘管就筆者所知,這樣的準則並沒有被明文記載在各大軟體廠的設計文件裡。此外,筆者也反對將這樣的標準用於判斷寫作者是否專業;亦不建議用手動處理這個程序:"The space bar is not a design tool"。

[2] 出處:知乎|https://www.zhihu.com/question/19587406

[3] 非正規的指南例如:Tunghsiao Liu|中文文案排版指北|GitHub。這份文件在2020年3月時累計了7500個GitHub星星。

[4] 在Liu的指南中主張中文標點與英文間不需額外墊上空格。這其實很主觀,但我們的函示仍然提供了設定的參數。

[5] 文中預設使用jQuery在前端執行轉換。如果你可以控制後端的原始碼,也可以將轉換放在伺服器端進行。另外,如果放在前端且你有使用其他JS工具去處理HTML(比如說程式碼標註工具),那麼你可能需要注意執行的先後順序;或是採用適當的CSS Selector去跳過不需要處理的區塊。

【程式】我需要學Python資料科學嗎?

近年來自學coding蔚為風潮,很常被朋友問到:「我需要學Python資料科學嗎?」。具體地說,被提出的課程通常包含了資料處理、視覺化,有的還包含了諸如網路爬蟲或是API串接[1]。

詢問者的動機通常是想充實專業,應用於學術或是工作領域。另一方面,也因為鋪天蓋地的媒體新聞與課程廣告,感到焦慮。

先講結論:個人認為,對於非資訊背景的學生,速成課程的學習效果相當有限;而大部分來詢問的人,未必真的需要;最後,儘管Python是個很棒的工具,但「會寫程式/Python」這件事,被包裝成了一個太過美好的想像。

而我的習慣是這樣的:如果一個大哉問被問了許多次;而又算是我有經驗的領域;我就會把它寫成文章。 所以我們就開始吧。

Python資料科學是什麼?

首先,請見附圖[2]。在這個結構裡,資料科學——或是一場有意義的數據專案(諸如行銷、選舉、乃至最近很常見的肺炎數據呈現),個人認為需要三個領域的完備:

  • 專項領域知識
  • 數學與統計學
  • 電腦應用 <= Python在這裡

Python Data Science

請特別注意前兩項的存在!首先,Python有許多套件,可以協助我們處理數據,實作出「專項領域知識」+「數學與統計學」的交集——然而在這個場景裡,Python本身並無法自外於兩者而產生意義。其次,Python的操作並不屬於太高深的計算機科學[3],你也不會因為只熟悉Python(或任何一種程式語言)而獲得一位(認真學習多年的)資訊本科生的同等能力;或是可以因此去Google開發無人車。比較好的比喻是:你可以把Python想像成威力更強大但也更不容易操作的Excel:它們都有很棒的資料處理能力,但要從資料中提取出價值,還是要倚賴領域本身的理論基礎才行。

再舉個幾個例子吧:電鍋很好用,但電鍋不會讓你成為大廚;一組完備的工具機,也不會讓你成為一個優秀的水電工;Python可以幫你處理股票資料,但不保證你能做出有意義的分析甚至盈利——而用Excel就做得風生水起的投資戶大有人在。

這段用詞比較負面,但我想透過這樣的措辭,來平衡那些課程廣告中過分美好的(他們也知道不是事實但仍然留下的)暗示。

所以⋯⋯我需要學Python資料科學嗎?

來詢問我的大多是商管學院出身的工作者。坦白說,我覺得最泛用的答案是「NO」,以及接著的「unless……」

僅管Python很棒,但根據個人經驗,對於非資訊本科生而言,一場成功的入門學習通常必須滿足以下(任一)條件:

  • 你是一個專業人士,你已經相當熟悉Excel或其他試算表軟體,且已經遭遇到Excel無法滿足的使用情境。
  • 你是一個專業人士,你現在的工作團隊已經在使用Python(或其他資料庫語言)。在有實務用例與演練的情況下,學習效率會有十倍百倍的提升。
  • 你是一個高中生或大學生,你擁有充足的學習時間,也正在處於廣泛探索知識的階段。在這個情境下,程式語言會是一個很棒的通識主題——同樣的道理我也會推薦你學習經濟學、社會學、數學、法律⋯⋯而一但開始學programming,別忘了務必多寫code以及實作對你有意義的專案,唯有如此才能真正內化這門技藝。
  • 你是一個研究生,你想要在研究領域裡實作量化資料分析,並將過程與方法有系統地與其他研究者分享。
  • 如果以上皆非;但你有充足的時間與意願學習這項技術;也有想要實作的應用(例如股票分析)。
  • 再次以上皆非;但你想要在履歷表放上這項專業;且你是個善於包裝的人;同時你相信你現在或未來的雇主崇拜/敬畏會寫code的人(即便沒有實務上的價值依然如此?)

Why?

對於學習Python說NO,這樣的建議其實是相當違反潮流的。我想原因如下:

  • 我沒有要賣你課程(超級100%正解)。
  • 儘管Python已經是相當高階的(面向人類而非機器的)程式語言,但對於一般沒有資訊背景的人而言,仍然是易學卻難通。在大部分強調速成的網路課程裡,為了提升學生的購買意願以及成就感,往往快速進入資料處理與視覺化的部分,而略過了基本的安裝、除錯、command line操作、版本控制、程式語言的基本概念;以及Python的資料結構、基本語法,與套件管理。這樣的課程雖然速成,但學生並不會獲得解決問題、有效率地寫code,以及舉一反三的能力。常見的結局是:學生只能拿著教材提供的代碼修修補補,但不具備真正實作專案的能力。如果又沒有實務上的用例,這樣零碎的知識很快就被會遺忘。
  • 如果你是一個已累積一定專業的工作者,我假定你的時間是寶貴的,且你的投入是期待回饋的。因此,除非已有明確的需求,散亂地學習的投資報酬率並不高。另外,不深入地學習也無法讓Python或資料分析成為你的第二專業——那些速成課程的內容對於資訊本科生而言,往往只是翻翻文件就(應該要)做得出來的東西。但無可取代的永遠是你的專項領域知識,以及對於數學及統計學的掌握。而這都是一般Python課程無法給你的。
  • 承上,Excel有什麼不足?如果你已經親身遭遇並能回答出來,那我相信你會獲得遠超過平均的學習成果。
  • 更不用說「Python+AI+大數據+爬蟲」的大雜燴課程了。這些課程就像健身房會籍一樣,絕大部分的人付費後並沒有妥善使用,而只是被販售恐懼以及購買美好的想像而已。並不是說課程本身有什麼過不去的惡點,但不論是投入任何專業領域,請問問自己:你真的準備好放下其他事、充足的補給、十足的熱情,以及不怕卡關的韌性了嗎?
  • 當然,如果你覺得自己只是想探索,也不介意花一點時間,那就出發吧。

怎麼學?

這個段落偏向主觀,有很多有經驗者見解也和我不同。如果你心意已決,請斟酌自已的個性與過去的學習經驗,作為參考即可。

首先,在「時間寶貴」與「實作優先」的兩項前提下,我推薦線上課程勝於實體課程。另外,個人是獨處與根性的信徒,如果你總是相信「有實體的活動或同學比較不會怠惰」,那我(偏狹地)猜測你可能不適合寫程式(笑)。

其次,如果語言與經濟能力許可,我建議選購英文的付費課程。長遠來說,習慣閱讀英文文件以及論壇,能達到最好的學習效果;另外,付費課程往往有更好的架構,可以避免初學者無法有效過濾學習素材的問題。個人用過的線上服務包含teamtreehouse.com與datacamp,都稱不上完美,但也絕對不至於帶來關鍵性的失敗。

另外,許多課程會提供網路介面與沙盒讓你寫code。儘管這都是不錯的初學工具,但你應該要很快地離開這些介面,快速掌握自己本機上的開發環境才行——如果你要寫實戰的code,總不可能寫在教學網站的編輯器裡對吧?

最後,別忘了Python是一個泛功能(general purpose)的程式語言。資料分析套件只是它眾多功能其中的一項而已。儘管是一個很棒的入門點,但個人建議你必須先懂Python,然後才能好好操作這些套件並讀懂文件——這包含了Python的基本語法、資料結構、函式應用、物件導向編程、套件管理等。另外,你可能也需要花時間熟悉command line以及版本控制等進階的電腦應用。

結語

對於非資訊背景者而言,想要有效學習Python資料分析,最好已經具備具體的需求,以及投入充足的時間(個人體感估計是:平均每週十小時,持續半年),才能累積出明顯的成效——別忘了,這意味著排擠其他生活中的事務,包含已有的專業、人際關係、休息與娛樂等⋯⋯

總而言之。寫程式對某種類型的人類會是很快樂的事。商人自有話術,商品未必邪惡,但coding絕非每個人都必須具備的技術。萬事皆有成本;而玩票近似於不玩——剩下的,就請自行斟酌了。


註解

[1] 嚴謹地說,這樣的課程其實是以應用為主的。稱之為科學太沈重了。

[2] 改編自:Jake VanderPlas著|2017|Python Data Science Handbook|第一章|O’REILLY發行

[3] 有一派主張「計算機工程」更精準於「計算機科學」。本文不討論此議題且並列兩者。

【翻譯】我們將在海灘上戰鬥(邱吉爾)

原文取自二戰時英國首相Winston Churcill在1940年發表的演說〈We Shall Fight on the Beaches〉。時值納粹德國席捲歐陸,希特勒試圖勸降英國,而邱吉爾斷然回絕。

我們不會頹喪或敗退。

…We shall not flag or fail.

我們將從一而終。我們將在法國戰鬥;我們將在近海與遠洋戰鬥;我們將在天空裡越戰越堅定、越戰越強;我們將捍衛我們的島嶼;不計代價。

We shall go on to the end. We shall fight in France, we shall fight on the seas and oceans, we shall fight with growing confidence and growing strength in the air, we shall defend our island, whatever the cost may be.

我們將在海灘上戰鬥;
我們將在登陸點上戰鬥;
我們將在田野與街頭中戰鬥;
我們將在丘陵間戰鬥;
我們絕不投降。

We shall fight on the beaches,
we shall fight on the landing grounds,
we shall fight in the fields and in the streets,
we shall fight in the hills;
we shall never surrender,

儘管我不信,但哪怕是這座島嶼或其大部分都淪陷且彈盡糧絕。那麼,我們在海外的帝國,被不列顛艦隊守護且武裝的帝國,亦將奮戰到底——直到上帝眷顧的時光來臨。在那個新世界裡,祂的力量與全能將拯救舊世界,並帶來自由。

and if, which I do not for a moment believe, this island or a large part of it were subjugated and starving, then our Empire beyond the seas, armed and guarded by the British Fleet, would carry on the struggle, until, in God’s good time, the New World, with all its power and might, steps forth to the rescue and the liberation of the old.

【香檳】菁英中心不收垃圾

常常覺得知識是很有趣的,但有時教育者本身並不關心人。即便是我這樣對程式有愛的學習者,到了CS學院上課,也常常覺得折磨⋯⋯

以下圖為例,取材自最近上課的講義。上方是投影片截圖,下方是我整理出來的筆記。這張投影片的主題是透過一個簡單的遞迴函式,示範OCaml語言的遞迴語法。

pic

這張投影片在課堂中大概只會使用30秒。但是否理解這30秒的內容,卻關乎接下來的學習效果。

計算原理就只是國中數學的乘法公式——乍看之下卻像是一種艱深定理的證明。但只要經過簡單的排版與上色,就可以大幅降低學生消耗的認知能量。我認為,不論你是不是程式熟手,都不該浪費能量在混亂的資訊中。

以下是我覺得可以做出的改變:

  • OCaml有許多宣告函式的語法,我換了一個最靠近C家族的(因為學生一定學過)。並透過排版與縮排讓他結構上更容易與已有的知識連結。

  • 使用等寬字來區分(可交給機器的)程式碼與(不可交給機器的)自然語言。

  • 使用等寬字後,我們可以輕易地做出上下整齊的排版空間。並將將後兩句switch概念的語法進行同質性的排比。第一個 | 符可加可不加,但個人偏好會加。

  • 使用桃紅色標明這則案例的關鍵字rec。

  • 使用橘色區分參數n。而n一但被標示出來,match與with便具備了關鍵字的隱喻。

  • 使用灰色標示註解——學生甚至不需要知道OCaml是使用(**)作為註解符這件事。

  • 使用黃色螢光筆標示出函式的遞迴點。

  • 井字號(#)為REPL工具的prompt字符,但其實在教學的上下文中,根本不需要使用——使用的結果就是讓學生copy代碼時如果沒有略過#,會得到錯誤。

最後還是想讚嘆Python當初把縮排納入語法的設計——這堪稱是科技對人類的無上關懷。考量到「程式更常被閱讀,而不是撰寫」這件事實,Python讓你無論怎麼惡搞,都得處理好排版;而一但你處理好了排版,其實也就沒那麼惡搞了⋯⋯

而世界的現實似乎就是,大學往往是教天才的,不是教笨蛋(如敝人)的。更廣一點來看,這其實也反映了大學體系強迫頂尖研究者同時也必須教學的弔詭之處。

【香檳】 A General Purpose Programmer

最近比較認真地思考著未來的求職,苦思一句新版的About Me用於履歷,想來想去,竟沒有比「A general purpose programmer」更好的——總之是個削瘦寡言的華人男性工程師。無臉孔,近似物質。要發射火箭那是不行;翻翻文件寫點不出格的代碼,持續累積知識,倒還算有點信心。

在美國也一年多了,漸漸地體驗到所謂「虛偽的美國人」的說法:一方面,他們強調多元文化與個人特色;但另一方面,其實每個人又忙碌得不得了,以至於只能勉力維繫價值的表象——作為一個潛在的海外移工,在此生活,人的屬性其實相當稀薄。

舉例來說,敝系iSchool是個國際學生非常多的學院——你要說是國際學生學店我也不介意[2]。其對於分組扮多元文化家家酒之熱衷,已經到了連教資料結構都要分組的境界。但是,其實不止教職員忙,連來美國拿身分與賺錢的台灣人中國人印度人,以及來敝系偷學Python的美國人其實也都很忙。不只課業負擔其重;還要抽空投履歷與面試,以至於分組的化學效應真的奇差無比。所謂的多元文化融合,也就是一件「我們極度重視,儘管它得到的資源排不進top 10」這般,如此重要的事。

再兼之美國的企業規模之大,我常笑稱,美國人總說:We are an agile startup with ONLY 500 employees。其人才需求之專業化程度,本質上就近似於軍隊。我感受到自己在台灣做過的工作,那些吾少也賤現在依然很賤的歲月裡所學會的鄙事,在這裡真的一點也派不上用場(噢你還做過遊戲設計和PM啊,那你會不會功夫或包餅乾呢)。

(岔個題,在台灣工作的朋友真的要慎重評估「待在小公司當小叮噹」這件事——儘管我本人不後悔,但還是建議大家慎思這件事對職涯可能的負面影響。除非你很堅定,否則想辦法擠進大公司擔任專職;又或是至少是小公司的專職;都還是相對安全很多的選擇)。

我對於美國人的所謂虛偽有一種私人的解讀暨體諒。記得曾看過一篇報導:「(在美國)除非你是比爾蓋茲,否則你離破產永遠只有一場大病的距離」。作為資本主義社會的模範,美國的專業人士一方面賺得真的很多;另一方面環境卻也內建了可能是世界上最強烈的消費主義與最進步的商業技術;同時,個人主義的盛行更讓社會無從建構出完善的福利制度,讓人得以在不幸時免於失去人之為人的基本尊嚴。

這樣生活的具現就是:人們賺得很多;但是想買的東西更多。幸福的背後就是萬丈深淵,因此無事不需保險——同時必須祈禱保險條款裡沒有致命性的漏洞。

正如《寄生上流》裡說的「如果我有錢,我也會是個好人」。所謂倫理道德暨多元文化包容,估計也都是吃飽撐著沒事的人兒發明出來維繫社會安定的東西。大家都有生存壓力,相同人種結合在一起,中文組刷著一畝三分地;美國組與印度組自己進行著神秘的計畫,其實才是再合理不過的事。題外話是,在我私人的理解裡,東亞的主流貌美女性,儘管仍然一定程度地受制父權價值的壓迫,但當在融入多元文化圈時,仍然是具有相當優勢的(東亞女生真的好美啊)。

而至於要不要真的用「A general purpose programmer」呢,我還是有點猶豫。就像敝業師曾苦心規勸本寶寶「A great programmers don’t result from people who only study programming」。所謂國王沒穿衣服是一件事,公然宣稱國王沒穿衣服,那又是另一件事——但我覺得最有可能的是,根本沒有人讀得出本寶寶心中的百轉千迴。看到這句子,起先覺得疑惑,但在30毫秒內便斷定了這人either英文不太好or腦子有問題,總之總之,真的不是很重要⋯⋯

以上,其實也沒什麼。新學期修了一門叫作Programming Language & Compiler的課,學習了一個讓人三觀盡毀的語言叫OCaml。感受到自己對於程式理論的鑽研即將要暫告一段落了——再往深處,就是所謂學術的巍巍大門了。而自己不論是求知慾與心智負擔,似乎也都將到了一個臨界。

連同申請算入,算是任性地低頭念了兩年半的書。儘管不大情願,還是要抬起頭關照社會、關照人。勉勵自己要慢慢開始恢復社交,多面試、多講英文與多交朋友。故以此為記。以上所描述的看法,說不定未來也會改變。


註解

[1] General purpose programming language是一個常見的對於程式語言的描述,意指該語言的目的不限於專門領域(例如Python與Java)。以Python為例,其被泛用於資料分析、自動化腳本、網站後端⋯⋯等。

[2] 但要澄清的是,這學店賣的不主要是學歷,主力商品反而是OPT。另外以美國的教育成本來看,敝系收費尚屬公道。

【香檳】時事有感

想想民主其實是一種很微妙的制度,它的精妙性似乎不全是當初制度與理論推行者,所刻意為之的。

舉例來說吧,太陽花事件裡馬政府最後出動了鎮暴警察與水炮車,為什麼最後沒有上綱到如香港般更大規模的全島癱瘓運動,以及隨之而來的催淚彈雨和恐怖政治呢?

因為光是選舉,就直接讓國民黨輸到脫褲。

這其中固然有內耗,有不效率,甚至看來愚蠢可笑。但很明確地,這成功避免了更多慘無人道的殺伐。而台灣一定程度上獨立的司法,更讓國家機器要形成一黨專政的利益集團更加困難——當馬英九無法透過司法,將王金平或是其他派系抄家時,我們就會看到一堆顧慮著選舉的跳船仔,以及一個至今還在國民黨內呵呵笑,不戰不降不談不和不走不死,亦絕不出力的魯洨王系。

民主制度裡固然有撈仔,有政客,也有距離所謂「理想公民」有極大差距的選民群體。但能讓人類在生物內建「人人為己」的驅力下,還能某種程度地避免因權勢競逐而陷入原始狀態,導致人類在戰爭與恐怖政治裡無人道地死去的,大概也只有民主政治了。

而什麼是人性?我說人性的本質就是「嚮往生但終需死」,其間有老病餓苦的過程。而得以免於老病餓苦與其憂慮的片段,謂之快樂。就像一台飛在空中但失去起落架的飛機,人人都在苦思著一個免於粉身碎骨的結尾。

就像聖經裡說的,那些最偉大的、得以讓義人前往天堂的善行,也不過就是「我餓了你們給我吃,渴了給我喝;赤身露體你們給我穿;我作客旅,你們留我住;我病了、我在監裡,你們來看顧我」

而所有器物與制度的發明,其實都是中立的。在我的看法裡,其中真正有價值的所謂「文明」,便是那些能讓更多人免於老病餓苦,抑或是至少肯認老病餓苦之為議題的創造——以這個觀點來看,文學大多是文明的,而計算機則為不可知。

我直覺地相信中國共產黨並不是傻子,近年來歷任的國家主席大致還能維持一個彈性內的均勢與平衡。只可惜傻子只需要一個,就足以毀了一整代人。習近平政權可能已經用強太過,導致他只能繼續用強。而前所未見的計算機科學所帶來來的AI與大數據監控系統,是他維持統治的希望所在。

獨裁之患,在於當政者聰明但又不夠聰明,在體系裡缺乏深思卻便恣意重構,再搞不定就hard coding硬上,還自以為效率與洋洋得意。但我說,不管蓋了什麼高樓鐵路,沒有什麼比迫害人民,並讓人民在恐懼、饑饉與疾病中死亡更可憎的罪惡了。

只願一切還有轉圜餘地,台港中這一代眾生,不要因為習政權的愚蠢而一併「攬炒」,玉石俱焚去了。

【香檳】關於建議這件事

今天和instructor討論選課。我和他表明我所有有關programming的課都沒選到而我甚至願意先選(非常簡單的)Python introduction以備不時之需,反正我可以配合課堂看書自學更深入的主題。

這位老美非常nice地提醒我,一個好的programmer不該心中只有coding,也應該同時充實其他領域的知識,像是其他科學與社會科學。

本寶寶聞言心中戈登一聲,心裡苦但是寶寶不說。想想自己大學是商學院畢業,亦認真修了不少文學歷史與經濟的課,被社會運動吸住了好一陣子,畢業後到遊戲產業當PM和遊戲企劃(然後才開始自學程式)。且研究所第一年系上偏社科的必修我也算競競業業地修完了。十餘年來在某些人眼中亦不過是鼯鼠五技而窮:「你應該先確立自己的核心價值是什麼,再談其他」。

沒有責怪這位老美的意思,畢竟他也是非常認真地寫信與我尬聊,在課堂上給我的指導也很盡責。只是感受到:這世界上能坦蕩蕩地給予他人建議的人,都還是本於一種所處的優勢啊。

而人生終究是自己的,要能不為這些言語搖擺,還是要透過深入了解自己與所生存的條件,才能同時感到虛心與堅定。

最後想到我有一個原則就是:除非對方要求,否則不主動給予他人意見——這點我常常還是沒有做好,希望以後可以做得更好。終究是人生越老,上下文也就越多,片段地給出指引,實無異於對那些與經驗甚或磨難的輕率啊。