守卫命令语言

守卫命令语言（GCL：Guarded Command Language）是艾兹赫尔·戴克斯特拉于1975年为谓词变换语义定义的一门语言。它以精简方式组合了编程概念，它们处在以某种实际的编程语言书写程序之前。它的简单性使得采用霍尔逻辑证明程序的正确性更加容易。

## 守卫命令

守卫（guarded）命令是守卫命令语言的最重要元素。在守卫命令中，同名字所说一样，这个命令是被守卫着的。这个守卫是先决条件，在这个语言被执行 (计算机)之前，它必须为真。在这个语句执行开始时，可以假定这个守卫为真。还有，如果这个守卫为假，这个语句将不被执行。使用守卫命令使得证明程序满足形式规定更加容易。这个语句也经常是另一个守卫命令。

### 语法

一个守卫命令是形如G → S的一个语句，这里的

- G是先决条件，叫做守卫（guard）。
- S是语句。

如果G为真，守卫命令可以简单的写为S。

### 语义

当在计算中遇到G的时刻，求值它。

- 如果G为真，执行S。
- 如果G为假，查看上下文来决定做什么（在任何情况下，都不执行S）。

## 跃过和中止

在守卫命令语言中，跃过（skip）和中止（abort）是非常简单也是非常重要的语句。中止是未定义指令：做任何事情。中止语句甚至不需要终止（terminate）。在公式化一个证明的时候，它被用来描述一个程序，在这种情况下证明通常失败。跃过是空指令：什么事情都不做。它被用在程序自身中，当语法要求一个语句的时候，而编程者却不想要机器改变状态 (计算机科学)。

### 语法

```
'''skip'''

'''abort'''
```

### 语义

- Skip: 什么事情都不做
- Abort: 做任何事情

## 赋值

将值指派给变量。

### 语法

```
v := E
```

或

```
v0, v1, ..., vn := E0, E1, ..., En
```

这里的

- v是程序变量。
- E是数据类型同于其对应变量的表达式。

## 串接

语句由分号来分割。

## 选择：if

选择（经常叫做“条件语句”或“if语句”）是守卫命令的列表，从中选取一个来执行。如果多于一个守卫为真，非确定性的选取一个语句来执行。如果没有守卫为真，结果是未定义的。因为至少一个守卫必须为真，空语句"skip"经常是需要的。

### 语法

```
'''if''' G0 &rarr; S0
 □ G1 &rarr; S1
...
 □ Gn &rarr; Sn
'''fi'''
```

### 语义

在执行一个选择的时候求值所有的守卫。如果没有守卫被求值为真，则选择的执行会abort，否则非确定性的选择其值为真的守卫之一，并执行对应的语句。

### 例子

#### 简单

用伪代码：

if a < b then c := True

else c := False

用守卫命令语言：

```
'''if''' a < b &rarr; c := true
 □ a ≥ b &rarr; c := false
'''fi'''
```

#### 使用skip

用伪代码：

if error = True then x := 0

用守卫命令语言：

```
'''if''' error = true &rarr; x := 0
 □ error = false &rarr; '''skip'''
'''fi'''
```

如果第二个守卫被省略，并且error = False，结果是abort。

#### 多个守卫为真

```
'''if''' a ≥ b &rarr; max := a
 □ b ≥ a &rarr; max := b
'''fi'''
```

如果a = b，要么a要么b被选取为极大值的新值，具有相同的结果。但是，在实现这个例子的时候，可能会发现一个比另一个更容易或更快。因为对于编程者这里没有区别，可以随意实现任何一种方式。

## 重复：do

重复会反复的执行守卫命令知道没有守卫为真。通常，这里只有一个守卫。

### 语法

```
'''do''' G0 &rarr; S0
 □ G1 &rarr; S1
...
 □ Gn &rarr; Sn
'''od'''
```

### 语义

当执行重复的时候所有守卫都求值。如果所有守卫都求值为假，则执行skip。否则非确定性的选取其值为真的守卫之一，并执行对应的语句，此后再次执行重复。

### 例子

#### 欧几里德算法

```
a, b := A, B;
'''do''' a < b &rarr; b := b - a
 □ b < a &rarr; a := a - b
'''od'''
```

这个重复在a = b时候结束，在这种情况下a和b持有A和B的最大公约数。

戴克斯特拉在这个算法中见到了两个无限循环`a := a - b`和`b := b - a`同步的一种方式，在这种方式下`a≥0`和`b≥0`保持为真。

#### 扩展欧几里德算法

```
a, b, x, y, u, v := A, B, 1, 0, 0, 1;
'''do''' b ≠ 0 &rarr;
 □ q, r := a '''div''' b, a '''mod''' b;
 □ a, b, x, y, u, v := b, r, u, v, x - q*u, y - q*v
'''od'''
```

这个重复在b = 0的时候结束，在这种情况下变量持有对贝祖等式：xA + yB = gcd(A,B)的解。

#### 非确定性排序

```
'''do''' a>b &rarr; a, b := b, a
 □ b>c &rarr; b, c := c, b
 □ c>d &rarr; c, d := d, c
'''od'''
```

这个程序在其中一个元素大于它的后续者的时候持续的置换元素。这个非确定性冒泡排序不比它的确定性版本更加有效率，但是易于证明：只要元素仍未有序它就不会停止并且它每步至少排序2个元素。

#### Arg max

```
x, y = 1, 1 
'''do''' x≠n &rarr;
 □ '''if''' f(x) ≤ f(y) &rarr; x := x+1
 □  □ f(x) ≥ f(y) &rarr; y := x; x := x+1
 □ '''fi'''
'''od'''
```

这个算法找到值1 ≤ y ≤ n，对于它给定整数函数是最大的。不只是这个计算而且最终状态都不是必然唯一确定的。

## 应用

### 构造正确程序

将观察的守卫命令的同余关系推广成格导致了精制演算。这已经机制化于形式化方法如B方法中，它允许从它们的规定形式化的导出程序。

### 异步电路

守卫命令适合于准延迟不敏感电路设计，因为重复允许不同命令选择的任意相对延迟。在这种应用之，在电路中驱动一个节点y的逻辑门构成自两个守卫命令如下：

```
PullDownGuard &rarr; y := 0
PullUpGuard &rarr; y := 1
```

这里的PullDownGuard和PullUpGuard是逻辑门输入的函数，它描述了什么时候这个逻辑门分别将输出拉下或拉上。不同于经典的电路评估模型，一组守卫命令的重复（对应于一个异步电路）可以准确的描述这个电路的所有可能的动态行为。

### 模型检查

守卫命令可以用在Promela编程语言中，它被用于SPIN模型检查器。SPIN验证并发软件应用的正确操作。

### 其他

Perl模块 Commands::Guarded实现了戴克斯特拉的守卫命令的一个确定性的校正变体。