热门问题
时间线
聊天
视角
ALGOL 68
来自维基百科,自由的百科全书
Remove ads
ALGOL 68(源自英语:ALGOrithmic Language 1968的缩写),是一种指令式编程语言,作为ALGOL家族的成员,它是官方上的ALGOL 60后继者。ALGOL 68的设计目标,是提供更广泛的应用,以及更严格的语法定义。
Remove ads
ALGOL 68的特征包括基于表达式的语法,用户声明的类型和结构与标签联合类型,变量与引用参数的引用模型,可变长数组和字符串、数组与矩阵的分片,用户定义的运算符和运算符重载,高阶函数与匿名函数,以及并发。
Remove ads
概论
IFIP工作组2.1在1964年3月达成一致,要制定ALGOL 60的两个后续语言标准:ALGOL X,它是具有增补的强类型和EULER语言中的树或列表的ALGOL语言重新定义[7],和ALGOL Y,它具有LISP的同像性风格修改自身程序能力[8]。经过多次会议研讨针对ALGOL X的提案之后,1968年12月20日,IFIP工作组2.1正式通过了ALGOL 68最终报告,并在1969年1月将其提交给了IFIP大会,在1969年3月,IFIP大会经过邮件投票授权出版此报告。
ALGOL 68的定义使用了阿德里安·范·韦恩加登发明的一种数学形式主义的两级形式文法[9]。van Wijngaarden文法使用分别称为“元产生式规则”和“超规则”的两组上下文无关文法规则,生成形成一个可能无限的产生式集合,而这些常规的产生式将识别特定的ALGOL 68程序;值得注意的是,它们能够表达在很多其他编程语言的技术标准中被标记为“语义”的那种要求,其他语言的语义必须用易致歧义的自然语言叙述来表达,并接着在编译器中实现为附加到形式语言解析器的“特设”代码。
ALGOL 68设计的主要目标和原则为:
在1970年,ALGOL 68-R成为了第一个投入工作的ALGOL 68编译器。在1973年9月确定并于1975年出版的修订报告中,省略了特定特征比如过程化、gomma和形式边界[10]。Stephen R. Bourne是ALGOL 68修订委员会成员,他选取了它的一些想法到他的Bourne shell之中。
ALGOL 68曾受到激烈的批评[12],最突出的是来自其设计委员会的一些成员比如C. A. R. Hoare[13],还有ALGOL 60编译器作者比如Edsger Dijkstra[14],它获评为抛弃了ALGOL 60的简单性,成为了复杂或过于笼统的想法的载体。ALGOL 68与刻意保持简单的同时代(竞争)者如C、S-algol和Pascal形成了鲜明对比。
ALGOL 68的语言定义出版后的文本长达两百多页并充斥着非既有标准的术语,这种复杂性使得编译器实现任务变得困难,故而它曾被称为“没有实现也没有用户”。这么说只是部分真实的,ALGOL 68曾应用于一些小众市场,特别是流行于英国的国际计算机有限公司(ICL)的机器之上,还有在教学角色之上。在这些领域之外,其使用相对有限。
尽管如此,ALGOL 68对计算机科学领域的贡献是深刻、广泛而持久的,虽然这些贡献大多只是在它们于后来开发的编程语言中重现之时才被公开认可。很多语言是为了应对设计这门语言时所采用的形式化方法导致的复杂性而专门开发的,其中最著名的是Niklaus Wirth的采用附缀文法的ALGOL W和作为其后继者的采用扩展巴科斯-瑙尔范式(EBNF)的Pascal[15];或者是针对特定角色而重新实现的,比如有些人认为Ada可以看作ALGOL 68的后继者[16]。
1970年代的很多语言可以追溯其设计至ALGOL 68,选取一些特征,并放弃被认为太复杂或超出给定角色范围的其他特征。其中就有C语言,它受到ALGOL 68的直接影响,特别是它的强类型和结构。多数现代语言都至少可以追溯其部分语法至要么C语言要么Pascal,因而很多语言可直接或间接的经由C语言而追溯至ALGOL 68。
ALGOL 68项目的完整历史可参见C. H. Lindsey的《A History of ALGOL 68》[17]。ALGOL 68语言的长篇阐述可参见Dr. Sian Mountbatten的《Programming ALGOL 68 Made Easy》[18],或者Marcel van der Veer的《Learning ALGOL 68 Genie》[19]。
Remove ads
规定和实现时间线
Remove ads
样例代码
下面是采用了ALGOL 68RS的程序结构的Hello World样例代码,严格的说只有在BEGIN
与END
之间的诸行是ALGOL 68程序,第一行、第二行和最后一行都特定于a68toc
编译器。
PROGRAM helloworld CONTEXT VOID
USE standard
BEGIN
print(("hello, world!", newline))
END
FINISH
第一行给出这个程序的标识为helloworld
而且这个名字的文件包含了这个程序;CONTEXT VOID
短语指定这个程序独立而非嵌入到其他部分之中。第二行的USE
加载了标准预置(prelude),print()
就在其中。将上述代码保存入文本文件helloworld.a68
中,然后使用ALGOL 68RS编译器a68toc
编译并接着执行它:
$ ca -u ASDFGHJ helloworld.a68
$ ./helloworld
hello, world!
下面是采用GCC的ALGOL 68前端ga68
程序结构的Hello World样例代码:
PROGRAM
BEGIN
puts ("Hello world\n");
0
END
这里的puts()
包含在POSIX标准预置之中,这里的0
是POSIX退出代码。将上述代码保存入文本文件helloworld.a68
中,然后使用ga68
编译器来编译并接着执行它:
$ ga68-15 -shared-libga68 -o helloworld helloworld.a68
$ ./helloworld
Hello world
下面的样例代码实现了埃拉托斯特尼筛法来找到小于等于100的所有素数。ALGOL 68中NIL
类似于其他语言中的空指针,x OF y
表示访问STRUCT y
的成员x
。
BEGIN # ALGOL68的素数筛法,基于链表实现 #
MODE
NODE = STRUCT (INT h, REF NODE t),
LIST = REF NODE;
OP CONS = (INT n, LIST l) LIST: HEAP NODE := NODE (n, l);
PRIO CONS = 9;
PROC one to = (INT n) LIST:
(PROC fn = (INT m) LIST: (m > n | NIL | m CONS fn(m + 1));
fn(1));
PROC error = (STRING s) VOID:
(print((newline, " error: ", s, newline)); stop);
PROC hd = (LIST l) INT:
(l IS NIL | error("hd NIL"); SKIP | h OF l);
PROC tl = (LIST l) LIST:
(l IS NIL | error("tl NIL"); SKIP | t OF l);
PROC show = (LIST l) VOID:
(l ISNT NIL | print((" ", whole(hd(l), 0))); show(tl(l))
| print(newline));
PROC filter = (PROC (INT) BOOL p, LIST l) LIST:
BEGIN
PROC fn = (LIST m) LIST:
IF m IS NIL THEN NIL
ELIF p(hd(m)) THEN hd(m) CONS fn(tl(m))
ELSE fn(tl(m))
FI;
fn(l)
END;
PROC sieve = (LIST l) LIST:
IF l IS NIL THEN NIL
ELSE hd(l) CONS sieve(filter(
(INT n) BOOL: n MOD hd(l) NE 0, tl(l)))
FI;
PROC primes = (INT n) LIST: sieve(tl(one to(n)));
show(primes(100))
END
将上述代码保存入文本文件primes.a68
中,然后使用ALGOL 68 Genie解释器执行它:
$ a68g primes.a68
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
Remove ads
显著的语言元素
标准语言包含六十一个保留字,典型的用粗体字打印,并且其中一些还具有“简短”符号等价者:
MODE
,OP
,PRIO
,PROC
,FLEX
,HEAP
,LOC
,LONG
,REF
,SHORT
,STRUCT
,UNION
,VOID
,BITS
,BOOL
,BYTES
,CHAR
,COMPL
,INT
,REAL
,SEMA
,STRING
,CHANNEL
,FILE
,FORMAT
,AT
@
,IS
:=:
,ISNT
:/=:
,OF
,TRUE
,FALSE
,EMPTY
,NIL
○
,SKIP
~
,COMMENT
CO
#
¢
,PRAGMAT
PR
,BEGIN ~ END
( ~ )
,IF ~ THEN ~ ELIF ~ THEN ~ ELSE ~ FI
( ~ | ~ |: ~ | ~ | ~ )
,CASE ~ IN ~ OUSE ~ IN ~ OUT ~ ESAC
( ~ | ~ |: ~ | ~ | ~ )
,FOR ~ FROM ~ TO ~ BY ~ WHILE ~ DO ~ OD
,PAR
,GO TO
GOTO
,EXIT
。
GO TO
被当作两个保留字。在未修订报告的语言中,OF
有等价符号 →
,EXIT
有等价符号□
,还有保留字EITHER
和IS NOT
结合。
Remove ads

↑
被替代为^
,←
被替代为_
。“值当字符”(worthy character)是1977年出版的ALGOL 68标准硬件表示报告出于可移植性而推荐的字符集中的字符[43]:
- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9
这反映了当年一些硬件不支持在ANSI的ASCII或IBM的EBCDIC之外其他字符的事实。
传输表示采用“值当字符”时,“加上乘以”符号⊥
替代为I
,“乘以的幂”符号⏨
和\
替代为E
。

ALGOL的“特殊”字符:
- × ÷ ≤ ≥ ≠ ≡ ¬ ∧ ∨ ⊃ ↑ ⏨ ␣ ↓ → ⊥ ○ □ ⌊ ⌈ ⎩ ⎧ ¢
多数都可以在1965年出现的配备APL打字球与键帽后的IBM 2741终端的键盘上找到。这些字符也是Unicode标准的一部分,并且在一些流行的字体中可获得到。
Remove ads
基本语言构造是“单元”(unit)。单元可以是“公式”、“封闭子句”、“例程正文”这样的构造,也可以是某个技术上需要的构造诸如赋值、跳转、越过和空无。
技术术语“封闭子句”(enclosed clause)统一了某些固有的加括号的构造,它们在其他当代语言中被称为“块”、“do语句”、“switch语句”等等。在使用关键字的时候,通常使用介入关键字的反转字符序列来终结这个包围,比如:IF ~ THEN ~ ELSE ~ FI
、CASE ~ IN ~ OUT ~ ESAC
和FOR ~ WHILE ~ DO ~ OD
。这种守卫命令语法被Stephen Bourne重新使用在常用的Unix Bourne shell之中。
一个表达式还可以产生多元值(multiple value)即有多个元素的一个值,它是通过“并立子句”(collateral clause)从其他一些值构造出来的。这种构造看起来就像过程调用的参数包(pack)。
Remove ads
数据类型在ALGOL 68用语中叫做“模态”(mode),其原始模态的声明符为:INT
、REAL
、COMPL
(复数)、BOOL
、CHAR
、BITS
、BYTES
和STRING
。ALGOL 68不再定义long
、short
和double
这样的类型,而是提供了修饰符(modifier)LONG
和SHORT
。
BITS
–BOOL
值的压缩向量(packed vector)。BYTES
–CHAR
值的固定大小的紧致多元组(即数组)。STRING
–CHAR
值的FLEX [1 : 0]
(声明时无元素)的灵活多元组(即数组)。LONG
– 声明INT
、REAL
或COMPL
的大小为LONG
。SHORT
– 声明INT
、REAL
或COMPL
的大小为SHORT
。
ALGOL 68提供了预置常量(prelude constant)如max real
和long max real
等来将程序适配到不同实现之中。
复杂模态可以使用模态构造子REF
、STRUCT
、UNION
和PROC
来创建自更简单的模态:
REF mode
– 到模态mode
的值的引用,类似于C/C++中和Pascal中的指针。STRUCT
– 用来建造结构,类似于C/C++中的struct
和Pascal中的recode
。UNION
– 用来建造联合,类似于C/C++和Pascal中的union
。PROC
– 用来指定过程,类似于C/C++中的函数和Pascal中的过程/函数。
在一个代码块中,诸声明不必须都先于诸语句,但所有名字都必须在使用前声明。ALGOL 68提供了恒等声明来将标识符绑定到常量值之上。在声明给名字的标识符之时,这个名字可以被初始化为立即引用一个值;还可以将名字声明为等同于以前声明的名字而使得二者互为别名。例如:
INT n = 2; # n是新建的常量,它被固定为2 # INT m := 3; # m是新建的局部变量的名字,这个变量的值被初始化为3 # INT p = m; # p是新建的常量,它被固定为同于m当前的值 # INT q := m; # q是新建的局部变量的名字,这个变量的值被初始化为同于m当前的值 # REF INT r = m; # r是新建的变量名字,它与m引用的是同一个变量 # REAL avogadro = 6.02214076E23; # 阿伏伽德罗常数 # LONG REAL long pi = 3.14159 26535 89793 23846 26433 832795; COMPL imaginary unit = 0 I 1; # 虚数单位i即复数0 + 1i #
其他声明符号包括:FLEX
、HEAP
和LOC
。
FLEX
– 所声明的名字是灵活的,就是说它可以引用任何数量元素的多元组(即数组)。HEAP
– 为变量从全局堆中分配一些空闲空间。LOC
– 为变量分配这个局部栈的一些空闲空间。
声明REAL x
只是REF REAL x = LOC REAL
的语法糖,LOC REAL
是生成REF REAL
模态的名字的单元。在REF REAL x = LOC REAL := 0
中,赋值LOC REAL := 0
导致整数0
在被赋值给这个生成器产生的名字之前就被扩充(widen)为实数0.0
,声明的名字x
与生成器产生的名字二者所引用的是同一个变量。
原始声明符还包括:FORMAT
、FILE
、CHANNEL
、SEMA
、COMPLEX
G和PIPE
G。
SEMA
– 可以通过运算符LEVEL
初始化的信号量。
ALGOL 68使用MODE
声明来给模态声明名字,它类似于C/C+中的typedef
和Pascal中的type
:
INT max = 99; MODE NEWMODE = [0:9][0:max]STRUCT ( LONG REAL a, b, c, SHORT INT i, j, k, REF REAL r);
这类似于如下C99代码:
const int max = 99;
typedef struct {
double a, b, c; short i, j, k; float *r;
} newmode[9+1][max+1];
对于ALGOL 68,只有模态标示(indicant)NEWMODE
出现在等号的左侧,最值得注意的是,构造的制造和读取,是从左至右而不顾及优先级的。还有ALGOL 68多元组(数组)的下界缺省的是1
,但也可以是从-max int
到max int
的任何整数。
模态声明允许递归模态,就是说模态可直接或间接的依据自身来进行定义。这要服从一些限制,例如下列声明是非法的:
MODE A = REF A; MODE A = STRUCT (A a, B b); MODE A = PROC (A a) A;
而下列声明是有效的:
MODE A = STRUCT (REF A a, B b); MODE A = PROC (REF A a) REF A;
Remove ads
注释可以按各种方式插入:
¢ 最初方式是在程序中增加两个美分符号 ¢ COMMENT "粗体"注释 COMMENT CO 风格i注释 CO # 风格ii注释 # £ 针对英国键盘的横杠/英镑注释 £
一般而言,注释在ALGOL 68中不能嵌套。这个限制可以使用不同的注释分界符来绕过。
语用指定(pragmat)是在程序中的编译指导,典型的提示给编译器;在新近的语言中叫做“pragma”。例如:
PRAGMAT heap=32 PRAGMAT PR heap=32 PR
ALGOL 68成为了面向表达式编程语言,赋值语句所返回的值是到目的地的引用。因此下列代码在ALGOL 68中是有效的:
REAL half pi, one pi; one pi := 2*(half pi := 2*arc tan(1))
这种表示法也出现在C语言和Perl以及其他一些语言之中。注意在早期语言比如ALGOL 60和FORTRAN中,在标识符中的空格是允许的,所以half pi
是一个单一的标识符,因而无需采用蛇形命名法或驼峰式大小写。
另一个例子,要表达数学中f(i)
从i=1
到n
的总和,下列ALGOL 68整数表达式就可充任:
(INT sum := 0; FOR i TO n DO sum +:= f(i) OD; sum)
注意,作为整数表达式,上述的代码块可以用在“整数值可以使用的任何上下”之中。代码块在ALGOL 68称为“系列子句”(serial clause),它返回其中被求值的最后一个表达式的值;这个概念也出现在LISP以及其他一些语言之上。系列子句加上封闭它的一对圆括号或BEGIN
与END
叫做闭合子句(closed-clause)。
复合语句都终止于独特的闭括号:
Remove ads
IF 条件 THEN 诸语句 [ ELSE 诸语句 ] FI 简短形式:( 条件 | 诸语句 | 诸语句 )
IF 条件1 THEN 诸语句 ELIF 条件2 THEN 诸语句 [ ELSE 诸语句 ] FI 简短形式:( 条件1 | 诸语句 |: 条件2 | 诸语句 | 诸语句 )
这种方案不仅避免了悬摆else问题,还避免了必须对嵌入的语句序列使用BEGIN
和END
。
CASE 接转 IN 诸语句, 诸语句, ... [ OUT 诸语句 ] ESAC 简短形式:( 接转 | 诸语句, 诸语句, ... | 诸语句 )
CASE 接转1 IN 诸语句, 诸语句, ... OUSE 接转2 IN 诸语句, 诸语句, ... [ OUT 诸语句 ] ESAC 简短形式:( 接转1 | 诸语句, 诸语句, ... |: 接转2 | 诸语句, 诸语句, ... | 诸语句 )
采用粗体符号的选择子句的例子:
PROC days in month = (INT year, month) INT: CASE month IN #一月# 31, #二月# IF year MOD 4 EQ 0 AND year MOD 100 NE 0 OR year MOD 400 EQ 0 THEN 29 ELSE 28 FI, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 # 直到十二月 # ESAC;
采用简短符号的选择子句的例子:
PROC days in month = (INT year, month) INT: (month| 31, (year%*4 = 0 AND year%*100 /= 0 OR year%*400 = 0 | 29 | 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
粗体和简短符号的选择子句可以混合使用。
ALGOL 68允许接转(switch)具有的类型,要么是INT
要么是(独有的)UNION
。后者允许在UNION
变量上施加强类型,参见后面的联合例子。
DO循环子句
[ FOR 索引 ] [ FROM 第一个 ] [ BY 增量 ] [ TO 最后一个 ] [ WHILE 条件 ] DO 诸语句 OD
循环语句的极小化形式是:DO 诸语句 OD
。
下面是这种“通用”循循的完整例子:
FOR i FROM 1 BY -22 TO -333 WHILE i*i /= 4444 DO ~ OD
这个构造有一些不寻常的方面:
- 只有
DO ~ OD
部分是强制性的,在这种情况下循环将无限迭代。 - 因此子句
TO 100 DO ~ OD
,将只迭代100次。 WHILE
“语法元素”允许编程者更早的从FOR
循环中破出,例如:
INT sum sq := 0; FOR i WHILE print(("So far:", i, newline)); sum sq /= 70**2 DO sum sq +:= i**2 OD
后续的对标准ALGOL 68的扩展,允许TO
语法元素被替代为UPTO
和DOWNTO
来达成小优化。这些编译器还结合了:
UNTIL
C – 用于更晚的循环终止。FOREACH
S – 用于并行的运算于多元组(数组)之上。
ALGOL 68支持多字段结构STRUCT
和标签联合UNION
。REF
可以前导于任何MODE
,包括多元组分片和结构字段。
作为递归模态的例子,下面是传统的链表的声明:
MODE VALUE = UNION (VOID, REAL, INT, COMPL, STRING), NODE = STRUCT (VALUE val, REF NODE next), LIST = REF NODE;
对上述VALUE
的UNION
使用共形子句(conformity clause)的CASE
的例子:
VALUE v := "1234"; # 或者 v := EMPTY; # CASE v IN (VOID): print(("void:", "EMPTY")), (REAL r): print(("real:", r)), (INT i): print(("int:", i)), (COMPL c): print(("compl:", c)), (STRING s): print(("string:", s)) OUT print("undefined value") ESAC
在未修订报告的语言中共形子句的功能通过在CASE
子句中采用共形关系CTAB
来实现。
在ALGOL 68中,不再采用术语数组或阵列(array),转而基于数学中的元组(-tuple)定义了多元值(multiple value)亦简称多元组(multiple)。多元组由一定数量的元素组成,每个元素都有相同的模态,有时称之为基础模态。多元组的模态由前导着一对方括号的针对每个元素的模态标示组成,它被读作“成行的模态”(row of mode),它不同于函数式编程语言如ML中的对应于代数数据类型里积类型的“元组”(tuple)。
ALGOL 68多元组的属性之一是它的维度数目。可以声明任何数目维度的多元组,整数的二维多元组拥有模态[,]INT
,它被读作“行套行的整数”(row-row-of-int),而实数的三维多元组拥有模态[,,]REAL
。
MODE VECTOR = [1:3]REAL; # 向量MODE声明 # MODE MATRIX = [1:3,1:3]REAL; # 矩阵MODE声明 # VECTOR v1 := (1,2,3); # 向量变量初始化为 (1,2,3) # []REAL v2 = (4,5,6); # 常量元组,类型等价于VECTOR,边界是隐含的 # OP + = (VECTOR a, b) VECTOR: # 二元OP即运算符定义 # (VECTOR out; FOR i FROM LWB a TO UPB a DO out[i] := a[i]+b[i] OD; out); MATRIX m := (v1, v2, v1+v2);
这里的冒号分隔构造:第一个元素的下标 : 最后一个元素的下标
,被称为裁剪器(trimmer)。
ALGOL 68的分片,在概念上涵盖了向多元组的各个维度加下标(subscript)从而访问其中特定元素的常规情况,但这个术语更常用的保留给至少一个维度不加下标的情况,比如从矩阵中切分出部分横行(row)或纵列(column)。例如:
REF VECTOR row = m[2,]; # 定义一个REF至矩阵的第二个横行 # REF VECTOR col = m[,2]; # 定义一个REF至矩阵的第二个纵列 # print ((m[,2:])); # 打印矩阵的从第二个至最后一个的纵列 #
省略了裁剪器中第一个下标则假定其为此维度的下界,而省略了第二个下标则将假定其为对应的上界,两个下标都省略则产生下界为1
的全部分片。
过程(procedure)的PROC
声明对参数和结果值二者要求类型指定,如果没有参数则省略参数列表,如果没有结果值则指定结果模态为VOID
:
PROC max of real = (REAL a, b) REAL: (a > b | a | b);
过程的返回值是在过程中求值的最后一个的表达式的值。当进行如下过程调用之时:
a := max of real(x, y)
得到等价于进行了如下变换后的结果:
a := REAL (REAL a = x, b = y; (a > b | a | b))
在这个过程之内对变量a
和b
的赋值是非法的。针对需要变更形式参数所提及的值的情况,ALGOL 68提供了传引用调用参数,用来在形式参数列表中指定引用,比如REF REAL
。到过程的引用REF PROC
也是允许的。下列例子定义一个过程,它将作为参数而指定的一个函数应用于一个数组的每个元素:
PROC apply = (REF []REAL a, PROC (REAL) REAL f) VOID: FOR i FROM LWB a TO UPB a DO a[i] := f(a[i]) OD;
这种代码的简单性是在ALGOL 68的前身ALGOL 60中不能达成的。
过程和运算符(operator)合称例程(routine),编程者可以定义新的运算符,并且这些自定义的和预定义的运算符二者都可以重载,并且它们的优先级可以由编码者变更。下列例子定义的运算符MAX
具有二元和一元形式二者,一元形式在一个数组的元素之上进行扫描。
PRIO MAX = 9; OP MAX = (INT a, b) INT: (a > b | a | b); OP MAX = (REAL a, b) REAL: (a > b | a | b); OP MAX = (COMPL a, b) COMPL: (ABS a > ABS b | a | b); OP MAX = ([]REAL a) REAL: (REAL out := a[LWB a]; FOR i FROM LWB a + 1 TO UPB a DO (a[i] > out | out := a[i]) OD; out);
初等单元(primary)涵盖封闭子句,此类属自身包括:所应用的标识符、调用、铸型、除了例程指示之外的指示和分片。
所应用的标识符(applied-identifier)意味着标识符在一个上下文之中被使用,并非处在其定义之中。
指示(denotation)是其所产生与任何行动都无关的构造,比如实数指示3.14
或字符串指示"abc"
,模态VOID
只有一个值,其指示是EMPTY
。在其他语言中,指示有时叫做“文字”(literal)或“常值”(constant)。
铸型(cast)构成自一个模态标示(indicant)和随后的通常为闭合子句的封闭子句。铸型可被用来提供强位置,在强上下文中能获得所有的强制。例如在REF REAL (xx) := 1
中的REF REAL (xx)
。
调用(call)引起(invoke)一个过程,调用在ALGOL 68 Genie中可以被部分参数化(partial parameterisation):就是说增加实际参数到一个过程的语境(locale)中,当这个语境完全之时调用这个过程,否则发生柯里化。
二等单元(secondary)包括生成器(generator)和选取(selection)。
有关的符号在技术上不是运算符,它们转而被当作“关联名字的单元”[44]。
三等单元(tertiary)包括公式和NIL
(可替代为○
)。公式构成自运算符和运算元。
在ALGOL 68中对于名字只有一个指示,那就是含义为不引用任何值的NIL
。NIL
的模态依赖于上下文,例如:REF INT k = NIL
,这里的NIL
拥有模态REF INT
。尽管NIL
是一个名字却不可以对它赋值。
LWB
(可替代为⌊
)和UPB
(可替代为⌈
),这两个运算符所运算的多元组运算元的模态是ROW
,它是指称任何横行的模态。在未修订报告的语言中,LWS
(可替代为⎩
)和UPS
(可替代为⎧
),分别是在一个多元组(数组)的这个维度的“下界状态”和“上界状态”为固定的(fixed)的情况下返回TRUE
。
四等单元(quaternary)包括:赋值、同一关系、例程指示(也称作例程正文)和SKIP
(可替代为~
)。四等单元并非语言报告中的术语,它是给从单元这个类属中除去封闭子句、初等单元、二等单元和三等单元所余下的只称为“单元”的类属的别名。
SKIP
产生任何模态的未定义的值,并且只能出现在强上下文中。
在ALGOL 68中除了恒等声明之外的所有构造都有一个值,它允许链式赋值,例如a := b := c
,这些赋值是从右至左执行的。
同一关系包括:IS
(可替代为:=:
)测试两个引用是否相等;ISNT
(可替代为:/=:
)测试两个引用是否不相等。同一关系的一侧是可以在强制时解引用来匹配另一侧的强侧,而另一侧则为软侧,不允许两侧都解引用。
强制(coercion)依据三个要件,从被强制者(coercend)产生已强制者(coercee):在应用任何强制之前作为要被强制者的一个先验模态,在这些强制之后作为已被强制者的一个后验模态,已强制者的的语法位置(position)或类属(sort)。强制是可以级联的(cascaded)。
有7种可能的强制,其术语为解过程化(deproceduring),解引用(dereferencing)和弱解引用(weakly-dereferencing),联合化(uniting),扩充(widening)、入行(rowing)和弃置(voiding)。除了联合化之外,每种强制都在所关联的值之上,规制(prescribe)一个相应的动态效果。因此,可以使用强制来编程很多原始行动。
允许特定强制的境况(circumstance)叫做上下文(context)。下面列出每种上下文所固有的强度及其允许的强制:
- 软(soft)– 解过程化。
- 弱(weak) – 软强制,随后弱解引用而产生一个名字。
- 柔(meek)– 软强制,随后解引用。
- 硬(firm)– 柔强制,随后联合化。
- 强(strong)– 硬强制,随后扩充、入行或弃置。
在任何上下文中,都有拥有或产生某种模态的值的一个单元,并且在这个上下文中亦有所需的一个模态。如果两个模态不同而现有模态的值能够强制成所需模态的值,则这种强制是合法的。
平衡是将IF
、CASE
和共形子句中的交替者(alternative)即可供选择者,和同一关系的两侧,都强制成共同(common)的模态的手段,这使得所涉及构造的上下文会被提升(promote)从而获得这种强制。
ALGOL 68有着上下文层级,它确定在程序中特定点之上可获得的强制的种类。这种上下文分为五个层次:
ALGOL 68文法的表达依据了一些原始概念:值、模态、上下文、强制和短语(phrase)。短语是声明和单元二者之一。共有5种上下文、7种强制、22种不同的单元,和潜在无穷数量的值和模态。在每种上下文中都有可获得的强制。
正交性所称谓的是基本概念可以被没有副作用的组合。原始概念:值、模态、上下文、强制和短语,是相互独立的,它们的组合给予了ALGOL 68少有编程语言拥有的灵活性。举个例子,如果需要模态INT
的一个值,比如在多元组的声明的裁剪器或边界中,那么在这个上下文中任何会产生一个整数的单元都可充任。其结果是ALGOL 68程序可以用宽广多样的风格来书写。例如打印从键盘读取的两个数的总和,用常规风格可以写为:
INT a, b; read((a, b)); print((a + b, newline))
也可以等价的写为:
print(((INT a, b; read((a, b)); a + b), newline))
只要所书写的是合法的ALGOL 68代码,可以采用编程者喜好的任何方式。这种独立性的另一个结果是语言的规则极少有例外。
传输(transput)是ALGOL 68用来称谓输入和输出设施的术语。它包括针对非格式化、格式化和二进制传输的预定义的过程。文件和其他传输设备以机器无关的方式来处理。下面的例子打印出一些非格式化的输出到“标准输出”设备:
print ((newpage, "Title", newline, "Value of i is ", i, "and x[i] is ", x[i], newline))
注意预定义的过程newpage
和newline
作为实际参数而传送。
print
和read
接受可能具有各种所需模态的实际参数,这些例程的形式参数具有成行的联合模态:
PROC print = ([]SIMPLOUT items) VOID: …… ; MODE SIMPLOUT = UNION ( INT, REAL, BOOL, CHAR, []INT, []REAL, []BOOL, []CHAR, [,]INT, [,]REAL, [,]BOOL, [,]CHAR, …… ); PROC read = ([]SIMPLIN items) VOID: …… ; MODE SIMPLIN = UNION ( REF INT, REF REAL, REF BOOL, REF CHAR, REF []INT, REF []REAL, REF []BOOL, REF []CHAR, REF [,]INT, REF [,]REAL, REF [,]BOOL, REF [,]CHAR, …… );
read
使用的模态SIMPLIN
是来自各种名字的模态的联合。print
使用共形子句来从参数行中每个元素提取出实际的值。
在ALGOL 68中“格式化传输”拥有自己的语法和模式(函数),具有嵌入在两个$
字符之间的FORMAT
。例如:
printf (($2l"The sum is:"x, g(0)$, m + n)); # 其打印相同于下列: # print ((new line, new line, "The sum is:", space, whole(m + n, 0))
ALGOL 68支持并行处理编程。使用关键字PAR
,可以将“并立子句”转换成“并行子句”,这里的行动的同步使用信号量来控制。并行行动在ALOGL 68 Genie中被映射到线程上,如果它们在宿主操作系统上能获得到的话。例如:
PROC eat = VOID: (muffins -:= 1; print(("Yum!", newline))), speak = VOID: (words -:= 1; print(("Yak...", newline))); INT muffins := 4, words := 8; SEMA mouth = LEVEL 1; PAR BEGIN WHILE muffins > 0 DO DOWN mouth; eat; UP mouth OD, WHILE words > 0 DO DOWN mouth; speak; UP mouth OD END
原本依据1968年最终报告的语言在模态铸型的语法上有所不同,并且它拥有过程化(proceduring)这个特征,就是说,将一个项目的值强制成求值这个项目的一个过程。过程化意图进行惰性求值。最有用的应用是实现布尔运算符的短路求值:
OP ANDF = (BOOL a, PROC BOOL b) BOOL: (a | b | FALSE); OP ORF = (BOOL a, PROC BOOL b) BOOL: (a | TRUE | b);
在ANDF
中,b
只在a
为TRUE
的情况下才求值。预期下面例子中的print
不会被执行:
IF FALSE ANDF (print("Should not be executed"); TRUE) THEN …… FI
语言修订之后,已经不再允许这种从模态BOOL
到模态PROC BOOL
的强制和铸型。ALGOL 68 Genie将ANDF
和ORF
作为扩展而实现为伪运算符。
在语言修订之前,编程者可以通过使用叫做“gomma”的分号替代逗号(comma),从而决定一个过程的参数串行而非并立的求值。例如:
PROC test = (REAL a; REAL b): …… ; test (x +:= 1, x);
这里保证第一个实际参数求值在第二个实际参数之前,但是在平常情况:
PROC test = (REAL a, b): …… ; test (x +:= 1, x);
这时编译器可以按它喜好的次序来求值实际参数。
参见
- ALGOL 60
- ALGOL Y
- ALGOL N
- C
- C++
- ALGOL 68与C++的比较
- Bourne shell
- Bash(Unix shell)
注释
参考文献
外部链接
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads