文化編程

莫看,莫看,莫看。重要的事說三遍,浪費時間我無論。

文化編程(Literate Programming)由計算機學界名宿 Donald Knuth 所創。其名,大陸譯爲「文學編程」,甚繆。將程序視爲蠻夷,以文教化,Literate Programming 之意大抵如是。git

文化,其目的在於實現人與人的溝通交流。假若這世上只有一人,文化是多餘的。同理,假若世上只有一個編程者,文化編程也是多餘的。程序的屬性分爲兩部分,一是供機器執行,二是供人類閱讀。程序能正確被機器執行,這是第一性,但程序要繼續維護下去,其代碼更易於爲他人理解,這是第二性,兩者缺一不可。因此,程序要有文化。github

Knuth 創立文化編程,江湖傳言,是報 E. W. Dijkstra 的一箭之仇。 Dijkstra 也是計算機學界名宿。當年,他提出告終構化編程,指出應當像設計機器那樣編寫程序,而 Knuth 寫的程序不夠結構化,或許爲此而不快。創立文化編程以後,Knuth 就能夠說 Dijkstra 沒文化,固然他不會這樣說。編程

江湖恩仇,兒女情長,這些事,提及來沒完沒了,到此爲止。這篇文章,真正的主角是我。segmentfault

Knuth 雖然提出了文化編程,惋惜的是,他創造的文化編程工具 CWEB 有歷史侷限性,如今不是很好用。這能夠理解,畢竟這個工具最初是他自用,而且在寫這個工具之時,也只有美國人有更多的機會使用計算機。工具

好在這種工具實現起來也不太難,去年過年的那段時間,我寫了一個相似的工具,名曰 orez,意思是 zero 翻轉。爲了防止百年以後有人再亂翻譯,我先將它翻譯爲逆零 :)ui

有文化的代碼

orez 基本上遵循 CWEB 的思路——程序的代碼與文檔,彼此獨立,但又混而爲一。若以泡茶爲喻,則代碼之於茶葉,文檔之於水,而 orez 之於茶壺。代碼與文檔的混合體,可稱之爲有文化的代碼。翻譯

代碼與文檔的混合須要藉助一組簡單的標記方能實現。下面,以一組五言詩句(姑且當其是)爲代碼,以詩句解析做爲文檔,演示這些標記的用法。這組詩句很直白,是一位無名氏,於一次磨刀以後隨手而做,記錄了磨刀的過程,讚美了磨過以後刀如何鋒利,最後感慨真正鋒利的刀無人賞識而隱沒於陋鞘。設計

代碼與文檔皆以段落的形式出現,而且老是以 @ 符號領起。例如:code

@ 一位俠士用水浸泡砥石,準備親手去磨已鈍了的未名刀:

@ 準備 #
滄浪三瓢水
砥石浴盆中
俠士自磨刀
霜刃名未名

兩者的區別是,代碼段落有名字,即 @# 之間的文字,例如「準備」。代碼段落的名字可根據實際狀況而取,此處僅僅是追求足夠簡單。文檔

假若有文化的代碼是以文檔開始,那麼首個文檔段落的領起符號可省略。例如:

俠士用水浸泡砥石,準備親手去磨已鈍了的未名刀:

@ 準備 #
滄浪三瓢水
砥石浴盆中
俠士自磨刀
霜刃名未名

若假設代碼段落以後總存在一個文檔段落,哪怕這個段落沒有任何內容,那麼文檔段落的領起符即可以在形式上做爲代碼段落的終止符。雖然沒必要非如此不可,可是在以文檔段落開始的狀況下,我更喜歡這種形式。例如:

俠士用水浸泡砥石,準備親手去磨已鈍了的未名刀:

@ 準備 #
滄浪三瓢水
砥石浴盆中
俠士自磨刀
霜刃名未名
@

這樣一來,若代碼段落後面是文檔段落,那麼該文檔段落領起符已存在。若代碼段落後面依然是代碼段落,那麼兩者之間會夾雜一個內容爲空的文檔段落,無礙。

接下來再繼續增長文檔段落與代碼段落:

磨刀,主要是靠腰部力量,手起到固定刀片傾斜角度的做用:

@ 姿式 #
力從足下起
腰旋若扭鬆
雙手互犄角
刃行掠風輕
@

刀片在砥石上的前進方向老是與鋒線垂直,而且須要時不時用手試
一下刀鋒是否已磨到卷邊的程度:

@ 技巧 #
鋒線有曲直
法向自無知
進退兩茫然
探手試微疵
@

假設全部的文檔段落與代碼段落皆已寫出,那麼最後可經過代碼段落的引用標記,將全部已給出的代碼段落彙總到一塊兒。例如:

@ 磨刀歌 #
# 準備 @

# 姿式 @

# 技巧 @

... ...
@

形如 # ... @ 這樣的結構,即是已給出的代碼段落的引用,亦即所引用的代碼段落的內容最終會嵌入到被引用處。同一個代碼段落,可於多處被引用。

貌離神合

假若不想爲每一個代碼段落都取個名字,畢竟取名字很煩人,能夠考慮使用同名代碼段,運算符 + 用於標記同名代碼段落內容鏈接起來,表示一個完整的代碼段落。

例如:

俠士用水浸泡砥石,準備親手去磨已鈍了的未名刀:

@ 磨刀歌 #
滄浪三瓢水
砥石浴盆中
俠士自磨刀
霜刃名未名
@

磨刀,主要是靠腰部力量,手起到固定刀片傾斜角度的做用:

@ 磨刀歌 # +
力從足下起
腰旋若扭鬆
雙手互犄角
刃行掠風輕
@

刀片在砥石上的前進方向老是與鋒線垂直,而且須要時不時用手試
一下刀鋒是否已磨到卷邊的程度:

@ 磨刀歌 # +
鋒線有曲直
法向自無知
進退兩茫然
探手試微疵
@

上述三個代碼段落,雖然分散各處,但它們其實是由兩個 + 運算符鏈接而成的一個總體:

@ 磨刀歌 #
滄浪三瓢水
砥石浴盆中
俠士自磨刀
霜刃名未名
力從足下起
腰旋若扭鬆
雙手互犄角
刃行掠風輕
鋒線有曲直
法向自無知
進退兩茫然
探手試微疵
@

+ 運算符表示自上而下順次鏈接同名的代碼段落,其逆運算符 ^+ 則表示自下而上鍊接同名的代碼段落。例如:

@ 磨刀歌 # ^+
磨刀歌

無名氏

@

這時,完整的代碼段落以下:

@ 磨刀歌 #
磨刀歌

無名氏

滄浪三瓢水
砥石浴盆中
俠士自磨刀
霜刃名未名
力從足下起
腰旋若扭鬆
雙手互犄角
刃行掠風輕
鋒線有曲直
法向自無知
進退兩茫然
探手試微疵
@

標籤

有時,須要將一個代碼段落鏈接到某個特定的同名代碼段落以前或以後,這時須要藉助標籤。

首先須要在目標段落上放一個標籤。例如:

@ 磨刀歌 #
<第一節>
滄浪三瓢水
砥石浴盆中
俠士自磨刀
霜刃名未名
@

<第一節> 即標籤。標籤必須置於代碼段落標題行的下一行,且獨佔一行。

在源段落中,將目標段落標籤與 +^+ 運算符組合起來,即可將源段落與目標段落鏈接起來。例如:

@ 磨刀歌 # <第一節> ^+
磨刀歌

無名氏

@

結果依然是:

@ 磨刀歌 #
磨刀歌

無名氏

滄浪三瓢水
砥石浴盆中
... ...
@

輸出

假設程序代碼與文檔內容混合體——有文化的代碼,存於文本文件 foo.orz。如今,若從中導出給人看的文檔——可喻爲倒茶喝,只需使用如下命令:

$ orez -w foo.orz -o foo.yml

生成的 foo.yml 是一份帶有排版信息的中間文件,它最終能轉化爲何文件,這取決於程序代碼與文檔內容是基於何種排版語言混而爲一。假若是基於 Markdown 語言,即可使用 orez-md 工具,將 foo.yml 轉換爲 foo.md 文件(Markdown 文件),而後,再用其餘工具將 foo.md 文件轉化爲網頁或其餘形式的文檔形式。

在 Linux 系統(或類 Unix 系統)中,從 foo.orz 到 foo.md 的整個過程,可濃縮爲一條命令:

$ orez -w foo.orz | orez-md > foo.md

foo.md 就是從 orez 這個茶壺嘴裏流出的茶水。

一壺茶喝到了沒滋味的時候,又該怎樣倒出茶葉呢?打開壺蓋,倒出來。不過,爲了更貼近 orez 的邏輯,須要將茶葉視爲袋泡茶,揪住袋上棉線,將茶葉從茶壺中拎出來。例如:

$ orez -t -e "磨刀歌" foo.orz -o mdg.txt

最後所得 mdg.txt 文件即是以 磨刀歌 爲棉線而拎出的袋泡茶。

尾巴

更多細節見 《orez 的故事》。之因此又重寫一篇簡略介紹,是由於之後要用 orez 的一些標記。

相關文章
相關標籤/搜索