ero 的故事 [2] = 秩序

虽然在 zero 文件中,代码片段并非是按照编译器的逻辑顺序分布的,但是 zero 提供了一些特殊的标记,通过这些标记可以随时将代码片段按照编译器或解释器的逻辑顺序提取出来。这是上一节『绕出』部分所讲述的内容。

我们在 zero 文件中实际上维护了代码片段的两种顺序,一种是人序,另一种是机序。人序是可见的,当我们从上而下逐行阅读 zero 文件或它经由排版软件产生的文档时,所看到代码片段的排列顺序便是人序。机序,是指代码片段按照程序代码编译器或解释器的运作规律所建立的顺序。在 zero 文件中,代码片段的机序由几个特殊符号来维护。

最基本的机序是代码片段的定义与引用。代码片段的定义,其基本格式如下:

@ 代码片段的名称 #
[... 代码片段 ...]
@
@ 是代码片段名称的起始符。# 是代码片段名称的终结符。

下面是一份来自 km.zero 的代码片段的定义:

@ 样点重新分类 #
empty_classes(class_tree);
assign_points(class_tree, points, env);
@
代码片段的名称可以是除 @ 与 # 之外的任意字符。代码片段必须以 @ 作为结尾。

每个代码都可以作为一个起点,从它开始,zero 能够绕出一份代码。只不过有的代码片段可以绕出一份很长的代码,有的只是绕出个作为起点的代码片段本身。譬如上面的『样点重新分类』这个代码片段,从它开始,绕出的代码是这个代码片段本身。可以使用下面的命令验证一下:

$ zero -e "样点重新分类" -o foo.c km.zero
若某个代码片段引用了其他代码片段,那么以它为起点能够绕出一份比它更长一些的代码。代码片段的引用格式如下:

... 被引用的代码片段的名称 ... @

与代码片段名称的格式相反,此时 # 变成了代码片段引用标记的起始符,而 @ 变成了代码片段引用标记的终结符。

例如,在 km.zero 中,上述的『样点重新分类』这个代码片段被另一个代码片段『agn_km.c』引用了,后者的定义如下:

@ agn_km.c # +

AgnTree
agn_km_classify(AgnList
points, size_t K, AgnArrayEnv env) {
AgnTree
class_tree = km_init(points, K, env);
while (!class_tree_stablized(class_tree, env)) {

样点重新分类 @

    }    return class_tree;

}
@
若以『agn_km.c』为起点,在绕出其代码的过程中,zero 会遇到代码片段『样点重新分类』的引用。此时,zero 会先绕出代码片段『样点重新分类』的内容,然后再继续绕出『agn_km.c』的内容。代码片段的引用链越长,zero 绕出的代码也就越长。

除代码片段的引用可以延长绕出的代码之外,还有一种同名代码片段的机制可以实现这种效果。所谓同名的代码片段,就是先定义一个代码片段,例如:

@ agn_points.c #
static AgnArray
point_copy(AgnArray
x) {
AgnArray y = agn_array_alloc(x->n);
for (size_t i = 0; i n; i++) {
double
xi = x->body[i];
double *yi = malloc(sizeof(double));
memcpy(yi, xi, sizeof(double));
y->body[i] = yi;
}
return y;
}
@
接下来,还可以定义同样名称的代码片段,但是代码片段名称终结符 # 之后必须跟随 + 或 ^+ 运算符。例如上文中已经出现的那份代码片段:

@ agn_points.c # +
static void
point_free(AgnArray *x) {
agn_array_free(x, free);
}
@
可将同名的代码片段视为一个数组中的元素。+ 表示将当前的代码片段追加到数组的尾部,而 ^+ 表示将当前的代码片段追加到数组的首部,它们只能出现在代码片段名称终结符 # 之后,且与 # 处于同一行。当 zero 对同名的代码进行绕出时,它会从这个数组中的首个元素开始,逐一进行代码绕出。

同名的代码片段,很有效缓解了我的命名恐惧症。在撰写 km.zero 的过程中,大部分代码片段都是同名的代码片段,主要借助 + 与 ^+控制它们的机序。

当我实现了 zero 对基于 + 与 ^+ 的同名代码片段的支持之后,不禁暗自佩服自己,竟然也能想出一些好主意。不过,很快就发现了问题,单凭 + 与 ^+ 来控制同名代码片段数组元素的次序,无法实现在数组的指定位置插入一个同名代码片段。于是,我又想出一个好主意,为代码片段附加一个标签。例如,上文出现的代码片段『agn_km.c』便是一份带标签的代码片段:

@ agn_km.c # +

AgnTree
agn_km_classify(AgnList
points, size_t K, AgnArrayEnv env) {
AgnTree
class_tree = km_init(points, K, env);
while (!class_tree_stablized(class_tree, env)) {

样点重新分类 @

    }    return class_tree;

}
@
便是标签,它只能出现于代码片段名称终结符的下一行。我们将这份代码片段称为代码片段『agn_km.c 』。

现在,若想将一个代码片段插入到代码片段『agn_km.c 』在同名代码片段数组中的位置之前,亦即前者在同名代码片段数组中的下标值比后者小 1,此时便可借助代码标签来实现这一目的,即:

@ agn_km.c # ^+
static bool
class_tree_stablized(AgnTree class_tree, AgnArrayEnv env) {

生成新旧种类中心集合 -> centers 与 new_centers @

    # 判断 centers 与 new_centers 是否相同 -> stablized @    agn_array_free(centers, (void (*)(void *))(env->free));    agn_array_free(new_centers, NULL);    return stablized;

}
@
代码片段标签在参与 + 或 ^+ 运算时,它们只能处于当前代码片段名称的终结符 # 之后,运算符之前。

有一个特殊的代码片段标签, 。这个标签不能附加任何代码片段上,但是它可以用于 + 或 ^+ 运算:若用于 +,表示将当前的同名代码片段追加到同名代码片段数组的首个元素之后;若用于 ^+,表示将当前的同名代码片段添加到同名代码片段数组首个元素之前。对于 C 代码片段而言,这个标签可以将一些不值一提的代码片段扔到 zero 文件末尾的附录部分。例如 km.zero 文件的末尾有以下代码片段:

@ agn_km.c # ^+

include

include

include

include "agn_km.h"

@
这样,就不用在正文部分去指出 agn_km.c 依赖哪些头文件了,从而避免正文部分受到这些细枝末节的内容的干扰。zero 所秉持的一个原则是,文档描述的是程序的主要矛盾,任何次要矛盾都应该扔到附录部分来说。如果你真的要用 zero,很快就会发现 是个很有用的设施。

既然有 这么特殊的标签,那么还有没有可以将当前的代码片段追加到同名代码片段数组的末尾元素的前后呢?答案是没有,因为同名代码片段的人序可以自然满足这一需求。

代码片段中出现的这些特殊的标记,在直接阅读 zero 文件或由它生成的排版作品时,可以开启人眼自动忽略模式,对它们视而不见。不过,有时也会希望看到这些标记,譬如某些代码片段的引用标记具有代码注释的效果。有时,也可以通过伴随 + 或 ^+ 运算的标签,确认某些代码片段的依赖关系。此外,对于由 zero 文件生成的排版作品,可能会将这些特殊标记转化为超级链接——HTML 与 PDF 文档都支持超级链接,从而实现在代码片段之间快速跳转。

关键字:文式编程


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部