algol语言
安德鲁·麦克格特里克(Andrew McGettrick)在其出色的教科书《 阿尔高68:第一和第二门课程》的序言中写道:
“本书源于1973-4年在斯特拉斯克莱德大学首次面向一年级本科生的演讲,其中许多人以前没有编程知识。许多学生不是将计算机科学作为主要课程,而只是将其作为一门课程。因此,他们是合适的听众,向他们授课,试图讲授Algol 68作为第一门编程语言。”
也许这句话对我来说尤为重要,因为我也是1973-1974年的一年级学生,尽管我在另一所大学-不列颠哥伦比亚大学。 此外,“追溯到那时”,UBC的入门级计算机科学课程在第二年使用Waterloo FORTRAN进行了讲授,并引入了一些IBM 360 Assembler。 没有什么比Algol 68更具异国情调了。就我而言,直到我三年级才接触Algol 68。 也许这次等待,再加上其他编程语言的经验,使我终生痴迷于这种被低估且精彩的编程语言。 多亏了Marcel van der Veer,他创建了一个非常好的Algol 68实现,称为Algol 68 Genie,现在终于在我的发行版资料库中,终于使我得以在闲暇时探索Algol 68。 我还应该提到,马塞尔的书《 学习Algol 68 Genie》对于新手和Algol 68的进修课程都非常有用。
因为我一直在重新发现Algol 68感到非常开心,所以我想我会分享一些想法和印象。
如果值得阅读Wikipedia上的Algol 68概述 ,那么真的值得阅读《算法语言Algol 68修订报告》中的这一段:
“原始作者很高兴地表示感谢,并感谢WG 2.1成员和许多其他对Algol感兴趣的人的全心全意的合作,支持,关注,批评和暴力反对。”
“批评和暴力反对”-哇! 实际上,一些委员会成员对委员会的发展方向非常不满意,以至于他们离开并开始了自己的语言定义项目,至少部分是出于对Algol 68的抗议。例如,Niklaus Wirth厌倦了Algol的复杂性68, 开始设计Pascal 。 在大约1984年至2000年左右编写并支持了相当多的Pascal代码后,我在这里告诉您,Pascal距离Algol 68尽可能远。 在我看来,这正是沃思的观点。
丹尼斯·里奇( Dennis Ritchie )在1993年在马萨诸塞州剑桥举行的第二届ACM编程语言历史会议上发表了演讲 ,他比较了Bliss,Pascal,Algol 68和C。在这次演讲中,他提出了一些有趣的观察:
关于Algol 68的更多观点在当今的互联网上仍然很突出。 很多都是负面的,但是哦! 我怀疑其中很大一部分都没有实际使用情况。 Rosetta Code Wiki上提供了一个非常有趣的地方,可以找到只是开始使用该语言的编码人员(还有许多其他人员,其中有些晦涩难懂)。 去那里,形成您自己的意见! 或关注我,因为我对Algol 68感到满意的地方不是那么好。
Algol 68作为一种编程语言,提供了一些独特而有用的思想,这些思想在当时具有创新性,并从那时起在某种程度上以其他语言出现。
设计Algol 68的委员会受到一套非常明确的原则的推动:
united
类型,类似于C中的union
类型) 与当今的动态类型化语言和弱静态类型化的语言相提并论,我发现将重点放在非常强大的静态类型化(50年前!)和预期的好处上具有启发性整个行业的运行时测试。 (好的也许这并不完全公平,但是它包含了一定的真理性)。
在用Algol 60和Pascal编写的程序中,我们看到了很多begin
和end
标记。 在C,C ++,Java等中,我们看到了很多{
和}
。 例如,可以使用Algol 60或Pascal将简单的表达式计算为整数值iv
的绝对值av
:
if iv < 0 then av := -iv else av := iv
如果我们想设置一个布尔值,说明iv
是否为负,那么我们需要开始插入begin
和end
:
if iv < 0 then begin av := -iv; negative := true end else begin av := iv; negative := false end
正式地,Algol 68对具有特殊含义(如if或then)的令牌使用粗体字,对诸如print ()过程之类的名称使用斜体。 在很多人仍在使用键穿Kong进行编码的时代,这是不切实际的,而今天仍然有些奇怪。 因此,Algol 68实现通常提供了一些标记特殊符号的方法(称为stropping ),而其他所有内容均未标记。 默认情况下,Algol 68 Genie使用大写字母加斜线,因此诸如if之类的符号被编码为IF,并且事物名称只能小写。 但是,值得注意的是,可以将一个名为“ if”的变量完全适合当前的目的完全可以。 Anway ...万一有读者倾向于复制/粘贴,我在代码示例中使用了Genie约定。
此外,Algol 68具有Bourne shell和Bash继承的封闭语法。 因此,Algol 68 Genie中的上一行代码将是:
IF iv < 0 THEN av := -iv; negative := TRUE ELSE av := iv; negative := FALSE FI
if
不明显,令牌fi
关闭前面的if
。 现在,也许我是世界上唯一曾经编写过如下所示的Java的人:
if
( something
)
statement
;
然后发现自己插入了对println
的调用以调试该代码:
if
( something
)
statement
;
System .
err .
println
( stuff
)
;
/* not in the then-part of if!!! */
无知地忘记将然后的部分包装在{
… }
。 当然,这还不是世界末日,但是当插入的结果不太明显时,可以说,多年来我花了很多时间调试这种事情。
但这在Algol 68中是不可能发生的。 Algol 68仍然需要begin
... end
才能进行操作员和过程声明。 但是, if
...... fi
, do
... od
和case
... esac
(大陵五的68 switch语句)全部关闭。
今天,我们在Go中看到了相同的概念; “ if”语句看起来像if ... {…}; {和}是必需的。 正如我已经提到的那样,Bourne shell及其后代使用类似的构造。
iv < 0
以上;
很明显会产生一个值,并且很可能该值是Boolean( true
或false
)。
所以那没什么大不了的。
但是赋值语句也会产生一个值,即赋值完成后赋值语句的左侧。
语句序列产生最终语句(或表达式)产生的值。
“ if”语句产生的是then-part或else-part的值,具体取决于“ if”之后的表达式是true
还是false
。
一个例子:考虑使用C,Java…三元运算符进行绝对值计算:
av = iv < 0 ? -iv : iv;
在Algol 68中,我们不需要额外的“三元运算符”,因为“ if”语句可以正常工作:
av := IF iv < 0 THEN -iv ELSE iv FI
这可能是个好时机,Algol 68使用( |
和)
提供了“简明”版本的符号,例如begin
, end
, if
, then
, else
等等。
av := ( iv < 0 | -iv | iv )
与前面的表达式具有相同的含义。
当我第一次遇到它时,令我惊讶的是循环没有产生一个表达式。 但是,循环有一些区别,一旦被完全理解,它们最终就变得有意义。
Algol 68中的循环可能看起来像这样:
FOR lv FROM 1 BY 1 TO 1000 WHILE 2 * lv * ly < limit DO … OD
此处的变量ly
是循环变量,由for
隐式声明为整数。 它的范围是整个for
... od
,其价值被保留从一个迭代到下一个。 我们可以在while
… do
part中声明一个常规变量,就像在if
… then
part中一样。 它的范围是while
… od
部分,但从一次迭代到下一次迭代都不会保留其值。 因此,例如,如果要累加数组元素的总和,则必须编写:
INT sum := 0; FOR ai FROM LWB array TO UPB array DO sum +:= array[ai] OD
其中运算符lwb
和upb
提供分别为数组定义的最小和最大索引值,并且+:=符号的含义与C或Java中的+ =相同。
如果我们想将总和作为一个值返回,我们可以这样写:
BEGIN INT sum := 0; FOR ai FROM LWB array TO UPB array DO sum +:= array[ai] OD; sum END
当然,为了简洁起见,我们可以用(
和)
代替begin
和end
。 此表达式将是过程(或运算符)的合理实现,该过程返回数组元素的值之和。
再次查看上面的表达式iv < 0
。
让我们退后一步,包括iv
的定义及其值的获取。 然后代码可能看起来像:
INT iv; read(iv); IF iv < 0 THEN … FI
但是,我们也可以这样写:
IF INT iv; read(iv); iv < 0 THEN … FI
在这里我们可以看到工作的正交性-变量的声明和读取可以在if
和测试变量的逻辑表达式之间进行,因为传递的值只是最终表达式的值。 此外,这与Algol 68语义一起提供了一个有趣的区别—在第一种情况下, iv
的范围是围绕“ if”语句的代码; 在第二个中,作用域介于if
和fi
。 按照我的想法,此选项意味着我们应该在远离变量使用的地方声明较少的变量,而真正保留的变量在代码中确实具有“长寿命”。
这也具有实际重要性。 例如,考虑使用某种SQL接口在数据库中执行多个脚本并返回值以进行进一步分析的代码。 通常,在这种情况下,程序员需要做一些工作来建立与数据库的连接,将查询字符串传递给execute命令,然后检索结果。 每个实例都需要声明一些变量来保存连接,查询字符串和结果。 这些变量可以在结果累积代码中本地声明时真是太好了! 这也有助于添加具有快速复制粘贴功能的新查询分析步骤。 是的,最好将这些代码片段转换为过程调用,尤其是在使用支持lambda(匿名过程)的语言时,以避免用重复的管理步骤混淆不同的分析步骤。 但是,具有非常本地定义的管理变量可以简化所需的重构工作。
正交性的另一个重要结果是,我们可以在赋值语句的左侧和右侧获得等效的三元运算符。
假设我们正在处理带符号整数的输入流,并且希望将正整数累积为增益,将负整数累积为损耗。 然后,以下Algol 68代码将起作用:
IF amount < 0 THEN losses +:= amount ELSE gains +:= amount FI
但是,这里无需重复+:= amount
; 我们可以将它移到if
… fi
,如下所示:
IF amount < 0 THEN losses ELSE gains FI +:= amount
之所以可行,是因为评估测试的结果是“ if”语句产生了损失或收益表达式,并且该表达式增加了数量。 当然,我们可以使用简短的形式,至少在我看来,这可以提高这些简短表达式的可读性:
( amount < 0 | losses | gains ) +:= amount
如何通过一个真实的例子来说明为什么这种以表达为导向的东西是如此的出色?
假设您正在编写哈希表工具。 您将必须实现的两个功能是“获取与给定键关联的值”和“设置与给定键关联的值”。
在面向表达的语言中,这些可以是一个功能。 为什么? 因为“获取”操作返回找到该值的位置,然后“设置”操作仅使用“获取”操作在该位置设置值。 假设我们创建了一个名为valueat
的运算符,该运算符valueat
两个参数-哈希表本身和键值。 然后,
ht VALUEAT 42
将返回键42在哈希表ht中的位置,然后
ht VALUEAT 42 := "the meaning of everything"
会将字符串“一切的意义”放在位置42。
这减少了支持手头应用程序所需的代码量,减少了必须测试的途径和边缘案例的数量,并且通常只为用户和维护者的生活增添了美好。
有一个简单的示例,该示例使用赋值语句左侧的过程将值存储在RosettaCode的表中。
如今,每个人似乎都希望使用匿名过程(或“ here”过程或lambda)。 Algol 68提供了开箱即用的功能,它确实非常有用。
举例来说,假设您想创建一种设施来读取带有分隔字段的文件,并为用户提供与它们的良好交互途径。 考虑一下awk
所做的出色工作,基本上是通过提取所有与打开文件有关的垃圾,读取行,将行拆分为字段,并在此过程中提供一些有用的附带变量(例如current-line-number),此行上的字段数,依此类推。
事实证明,这在Algol 68中也很容易做到,其中的任务是编写一个带有三个参数的过程-第一个是输入文件名,第二个是字段分隔符字符串,第三个是一个处理每一行。
该过程的声明可能如下所示:
PROC each line = # 1 #
(STRING input file name, CHAR separator, PROC (STRING, [] STRING, INT) VOID process) # 2 #
VOID: BEGIN # 3 #
FILE inf; # 4 #
open(inf, input file name, stand in channel); # 5 #
BOOL finished reading := FALSE;
on logical file end (inf, (REF FILE f) bool: finished reading := TRUE); # 6 #
INT linecount := 0; # 7 #
WHILE # 8 #
string line;
get(inf,(line, new line));
not finished reading
DO # 9 #
linecount +:= 1;
FLEX [1:0] STRING fields := split(line, separator);
process(line, fields, linecount)
OD;
close(inf) # 10 #
END # 11 #
这是上面发生的事情:
注释1(上面的#1#)-过程的each line
的声明(请注意,可以随意在名称或数字的中间插入空格)
到每个线的参数string
文件名,字段分隔符char
ACTER,和pro
cedure被调用来处理每一行,它本身需要一个string
(输入的线)的阵列string
S(的字段行)和一个int
(行号),并返回一个void
值
each line
返回一个void
值,过程主体以begin
,这使我们可以在其定义中使用多个语句
声明输入file
将standard input channel
与file
(其名称由input file name
指定)关联,然后将其打开(以供读取)
Algol 68处理文件结束条件的方式有所不同; 在这里,我们使用on logical file end
的I / O事件检测过程来设置标志finished reading
,我们可以在处理文件时检测到该标志
创建并初始化行数(请参见前面关于循环性质的描述)
此while
循环尝试从输入文件中读取下一行。 如果成功,它将处理该行; 否则,退出
处理输入行-增加行数; 使用split
过程创建一个与行的字段相对应的字符串数组; 调用提供的process
过程以消耗该行,其字段和行数
记得close
文件
过程定义的end
。
我们可能会像这样使用它,以便构建查找表(结合上一节中提到的假设哈希表工具):
# remapping definitions in remapping.csv file #
# new-reference|old-reference #
# 093M0770371|093X0012250 #
# 093M0770375|093X0012249 #
# 093M0770370|093X0012133 #
HASTABLE ht := new hashtable;
each delimited line("test.csv", "|", (STRING line, [] STRING fields, INT linecount) VOID: BEGIN
STRING to map := fields[1], from map := fields[2];
ht VALUEAT from map := to map
END);
在上方,我们看到了对每个分隔行的调用。 特别令人感兴趣的是声明“这里”过程或lambda,它将查找值存储到哈希表中。 在我看来,这里的一个重要教训是λ是Algol 68正交性的结果。 我认为那很整洁。
在我继续探索Algol 68时,我打算深入研究的一件事是,我可以进一步采用这种功能形式的表达方式。 例如,我看不到为什么不能逐个构建列表或哈希表元素,并由于循环过程的结果而产生完成的结构,所以上面看起来更像:
HASHTABLE ht := each delimited line as map entry("test.csv", "|",
(STRING line, [] STRING fields, INT linecount) VOID: BEGIN
STRING to map := fields[1], from map := fields[2];
(from map, to map)
END);
为什么要学习古老的,尘土飞扬的和被遗忘的语言? 好吧,我们都知道了最近对COBOL的兴趣,但是从某种意义上说,也许可能不是很多用SNOBOL,Icon,APL甚至Algol 68编写的关键任务应用程序。乔治·桑塔亚那(George Santayana)要牢记的指导是: “那些不记得过去的人将被谴责以重蹈覆辙。”
对我来说,使用Algol 68开发游戏有一些关键原因(可能还有其他几种语言,这些语言似乎对我的日常工作不是绝对必要的):
Algol 68并未定义为对现有编程语言中某些烦恼的React; 相反,根据修订报告:
该委员会(国际信息处理联合会ALGOL工作组2.1)“表达了对为许多国家的许多人服务的通用编程语言的价值的信念。”
“ Algol 68并不是作为Algol 60的扩展而设计的,而是基于对计算的基本概念和新的描述技术的新见解而设计的全新语言。”
无论是通过复制到其他语言中的积极贡献(在Bourne shell中do
… od
;在C,Java中……实现=)还是负面React(Pascal及其所有后代,Ada),Algol 68都可以声称对计算产生了深远的影响。
尽管Algol 68很大程度上是“那个时代的孩子”,但受到按键和行式打印机,小型多样的字符集,1960年代和1970年代计算机的字符和单词大小的广泛变化的影响,并且没有明确地包含面向对象的知识。或函数式程序设计,其相当出色的正交性和面向表达的功能弥补了这些怪异之处,并缺乏其他有用的方式。
也许最实际的原因是在桌面上安装并运行了出色的Algol 68 Genie解释器,让我可以从事这个奇怪的小嗜好!
也许我应该回到桑塔亚纳(Santanana)作最后评论:
“我们认为美是难以形容的:它是什么或它的含义永远不能说。”
翻译自: https://opensource.com/article/20/6/algol68
algol语言