热门问题
时间线
聊天
视角
ALGOL 68
来自维基百科,自由的百科全书
Remove ads
ALGOL 68(源自英語:ALGOrithmic Language 1968的縮寫),是一種指令式程式語言,作為ALGOL家族的成員,它是官方上的ALGOL 60後繼者。ALGOL 68的設計目標,是提供更廣泛的應用,以及更嚴格的語法定義。
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~,COMMENTCO#¢,PRAGMATPR,BEGIN ~ END( ~ ),IF ~ THEN ~ ELIF ~ THEN ~ ELSE ~ FI( ~ | ~ |: ~ | ~ | ~ ),CASE ~ IN ~ OUSE ~ IN ~ OUT ~ ESAC( ~ | ~ |: ~ | ~ | ~ ),FOR ~ FROM ~ TO ~ BY ~ WHILE ~ DO ~ OD,PAR,GO TOGOTO,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、COMPLEXG和PIPEG。
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來達成小優化。這些編譯器還結合了:
UNTILC – 用於更晚的循環終止。FOREACHS – 用於並行的運算於多元組(數組)之上。
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