宏,过程与标签
TLA+在多进程之间复用代码,最简单的方法是使用宏和过程。
宏
宏是通用的代码内联器,格式为
nacro Name(var1,...)
begin
\* stuff
end macro;
需要注意的是:宏代码是内联的,不能像进程那样独立运行,其实现不能有标签,不能有多重赋值,不能有while循环等。
宏定义必须要在算法的define块之后,在process块之前,其调用方式为Name(arg1,…)。
过程
过程的编写和使用有两个好处:1、过程可以有局部变量,2、可以添加标签,格式如下:
procedure Name(arg1, ...)
variables var1 = ... \* This cannot be \in, only =
begin
Label:
\* stuff
return;
end procedure;
过程的调用方式是call Name(arg1,…); 需要注意的是要想使用过程,需要EXTEND Sequences,否则在将pluscal转化为TLA+时会报错。
过程中可以使用标签,但是同一个标签下每个变量只能修改一次(这一点很重要),如果有修改两次的操作,会报缺少label的错误。。
过程的定义在宏定义块之后,在进程块之前。
建议
宏与过程都可以用来复用代码,减少代码量,相比较,宏更快,更简单。当有多个可以执行相同操作的单独进程时,尽量使用过程。
标签
每一个标签代表一个操作,因此标签决定了规范的原子度。这里有一些使用标签的规则
1、进程的第一行必须有一个标签,即在begin后跟的一定是一个标签。
2、标签必须在while之前,while内不能有标签
3、标签在使用时,必须紧跟在call ,return ,goto 之后。
4、在控制结构中,如if,either,如果可能的分支有标签,那么整个控制结构必须跟一个标签。
5、不能在with结构中使用标签
6、不能在标签中多次给同一变量赋值,例如下面两种情况,第一种可行,第二种不可行
Valid:
either x := 1; \* either or 是两个分支,可以分别使用x
or x := 2;
end either;
Invalid: \* 不能使用x进行两次赋值
x := 1;
x := 2;
如果标签中涉及结构体的多次赋值可以使用|| 进行分隔,如
Invalid: \* 不可行,
struct.key1 = 1;
struct.key2 = 2;
Valid:
struct.key1 = 1 || struct.key2 = 2; \* 用||分隔可行
在将pluscal转化为TLA+ 之后,TLA+代码中会增加一个pc变量,表示程序计数器,用来跟踪标签的变化,同时会在每个进程结束时自动定义一个“Done”标签,因此“Done”标签是不能自定义的,但是可以在pluscal代码中直接使用,例如goto Done。