文化编程

1
莫看,莫看,莫看。重要的事说三遍,浪费时间我不管。

文化编程(Literate Programming)由计算机学界名宿 Donald Knuth 所创。其名,大陆译为「文学编程」,甚缪。将程序视为蛮夷,以文教化,Literate Programming 之意大抵如是。

文化,其目的在于实现人与人的沟通交流。倘若这世上只有一人,文化是多余的。同理,倘若世上只有一个编程者,文化编程也是多余的。程序的属性分为两部分,一是供机器执行,二是供人类阅读。程序能正确被机器执行,这是第一性,但程序要继续维护下去,其代码更易于为他人理解,这是第二性,二者缺一不可。所以,程序要有文化。

Knuth 创立文化编程,江湖传言,是报 E. W. Dijkstra 的一箭之仇。 Dijkstra 也是计算机学界名宿。当年,他提出了结构化编程,指出应当像设计机器那样编写程序,而 Knuth 写的程序不够结构化,或许为此而不快。创立文化编程之后,Knuth 就可以说 Dijkstra 没文化,当然他不会这样说。

江湖恩仇,儿女情长,这些事,说起来没完没了,到此为止。这篇文章,真正的主角是我。

Knuth 虽然提出了文化编程,可惜的是,他创造的文化编程工具 CWEB 有历史局限性,现在不是很好用。这可以理解,毕竟这个工具最初是他自用,并且在写这个工具之时,也只有美国人有更多的机会使用计算机。

好在这种工具实现起来也不太难,去年过年的那段时间,我写了一个类似的工具,名曰 orez,意思是 zero 翻转。为了防止百年之后有人再乱翻译,我先将它翻译为逆零 :)

有文化的代码

orez 基本上遵循 CWEB 的思路——程序的代码与文档,彼此独立,但又混而为一。若以泡茶为喻,则代码之于茶叶,文档之于水,而 orez 之于茶壶。代码与文档的混合体,可称之为有文化的代码。

代码与文档的混合需要借助一组简单的标记方能实现。下面,以一组五言诗句(姑且当其是)为代码,以诗句解析作为文档,演示这些标记的用法。这组诗句很直白,是一位无名氏,于一次磨刀之后随手而作,记录了磨刀的过程,赞美了磨过之后刀如何锋利,最后感慨真正锋利的刀无人赏识而隐没于陋鞘。

代码与文档皆以段落的形式出现,并且总是以 @ 符号领起。例如:

@ 一位侠士用水浸泡砥石,准备亲手去磨已钝了的未名刀:

@ 准备 #
沧浪三瓢水
砥石浴盆中
侠士自磨刀
霜刃名未名

二者的区别是,代码段落有名字,即 @# 之间的文字,例如「准备」。代码段落的名字可根据实际情况而取,此处仅仅是追求足够简单。

倘若有文化的代码是以文档开始,那么首个文档段落的领起符号可省略。例如:

侠士用水浸泡砥石,准备亲手去磨已钝了的未名刀:

@ 准备 #
沧浪三瓢水
砥石浴盆中
侠士自磨刀
霜刃名未名

若假设代码段落之后总存在一个文档段落,哪怕这个段落没有任何内容,那么文档段落的领起符便可以在形式上作为代码段落的终止符。虽然不必非如此不可,但是在以文档段落开始的情况下,我更喜欢这种形式。例如:

侠士用水浸泡砥石,准备亲手去磨已钝了的未名刀:

@ 准备 #
沧浪三瓢水
砥石浴盆中
侠士自磨刀
霜刃名未名
@

这样一来,若代码段落后面是文档段落,那么该文档段落领起符已存在。若代码段落后面依然是代码段落,那么二者之间会夹杂一个内容为空的文档段落,无碍。

接下来再继续增加文档段落与代码段落:

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

@ 姿势 #
力从足下起
腰旋若扭松
双手互犄角
刃行掠风轻
@

刀片在砥石上的前进方向总是与锋线垂直,并且需要时不时用手试
一下刀锋是否已磨到卷边的程度:

@ 技巧 #
锋线有曲直
法向自无知
进退两茫然
探手试微疵
@

假设所有的文档段落与代码段落皆已写出,那么最后可通过代码段落的引用标记,将所有已给出的代码段落汇总到一起。例如:

@ 磨刀歌 #
# 准备 @

# 姿势 @

# 技巧 @

... ...
@

形如 # ... @ 这样的结构,便是已给出的代码段落的引用,亦即所引用的代码段落的内容最终会嵌入到被引用处。同一个代码段落,可于多处被引用。

貌离神合

倘若不想为每个代码段落都取个名字,毕竟取名字很烦人,可以考虑使用同名代码段,运算符 + 用于标记同名代码段落内容连接起来,表示一个完整的代码段落。

例如:

侠士用水浸泡砥石,准备亲手去磨已钝了的未名刀:

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

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

@ 磨刀歌 # +
力从足下起
腰旋若扭松
双手互犄角
刃行掠风轻
@

刀片在砥石上的前进方向总是与锋线垂直,并且需要时不时用手试
一下刀锋是否已磨到卷边的程度:

@ 磨刀歌 # +
锋线有曲直
法向自无知
进退两茫然
探手试微疵
@

上述三个代码段落,虽然分散各处,但它们实际上是由两个 + 运算符连接而成的一个整体:

@ 磨刀歌 #
沧浪三瓢水
砥石浴盆中
侠士自磨刀
霜刃名未名
力从足下起
腰旋若扭松
双手互犄角
刃行掠风轻
锋线有曲直
法向自无知
进退两茫然
探手试微疵
@

+ 运算符表示自上而下顺次连接同名的代码段落,其逆运算符 ^+ 则表示自下而上连接同名的代码段落。例如:

@ 磨刀歌 # ^+
磨刀歌

无名氏

@

这时,完整的代码段落如下:

@ 磨刀歌 #
磨刀歌

无名氏

沧浪三瓢水
砥石浴盆中
侠士自磨刀
霜刃名未名
力从足下起
腰旋若扭松
双手互犄角
刃行掠风轻
锋线有曲直
法向自无知
进退两茫然
探手试微疵
@

标签

有时,需要将一个代码段落连接到某个特定的同名代码段落之前或之后,这时需要借助标签。

首先需要在目标段落上放一个标签。例如:

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

<第一节> 即标签。标签必须置于代码段落标题行的下一行,且独占一行。

在源段落中,将目标段落标签与 +^+ 运算符组合起来,便可将源段落与目标段落连接起来。例如:

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

无名氏

@

结果依然是:

@ 磨刀歌 #
磨刀歌

无名氏

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

输出

假设程序代码与文档内容混合体——有文化的代码,存于文本文件 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 的一些标记。


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

载入中...