面向对象的符号在本书中得以进一步体现是很有必要的,我通过命令也称为指令来进行运算,(“声明”这个词语一般是用在这种功能里,但是我们我们不应该对它产生这样的误解:声明是一种实利表达,而不是一条命令) 除了一些特殊的循环结构外,为了使程序更更容易理解,结构化对于那些有现代编成语言(Algol 或者 Pascal或者 Ada 或者是C及派生出来的语言)基础的人来说无疑是非常熟悉的方法。这种方法包括:程序调用,分配,创建,条件,条件分支,循环,调试,重试,任务调试。
一般的程序调用包括可能有当前焦点的程序,输出控制将应用于被调用的那个程序上,在类B里面调用一个参数F,参数F只能在B里面起作用,而不能作用到A类里面,例如参数(X,Y。。。。。)其中一个或多个属于A类,或是A的父类,那么 为了理解第二条规则的原因,在C类里面由bq q1 q2 …。 组成的调用只有满足以下条件,它才有是有效的:
条件一:在第一个值后面,还必须能被C访问到
条件二:在多值调用里, 第二个点后每个成员变量,当i〉I每个q i ,必须可以在C类中访问到。
b:= aqq; c:= bqr; cqs
这条语句有效,只有当q,r,和s 都能被c 类访问到,在这个类的程序段里不管是R可以被基础类用到还是S 可以被R用到,它们之间 是不相关的 , 你可以在程序段的中间 或者开始条用 它, a + b 这个表达式可以被调用也可以把它写成 aqplus (b),对于程序组成来说这两种写法都是合法的 。
赋值的语法如下:
x:= e
X是一个实体变量,e 是变量表达式的一个取值 ,实体变量满足以下两个条件:
一。它在类里有非常量的属性
二。当前的普通实体变量包括了函数的返回值。
另一方面,非实体变量也有一点实体变量的特征(比如声明变量的时候是相同的:如声明0 INTEGER is 0)和我们看到的一样 ,这些正常的方法也许不能给变量赋上新的值。
创建结构在早期的文献里有两种形式,一种是没有程序如in!! x,另一种是由程序,如in!! xqp (…)。在这两种类型中 X 必须是变量实体,一些条件语句一般用于有几种不同类型的判断结果,根据不同产生不同结果,最基础的结构如下:
if boolean_expression then
instruction;instruction; …
else
instruction;instruction; …
end
每一条分支都有对应的程序段,也可以为空。如果第一个bool表达式的值为真,则执行第一个分支后的程序段 ,否则判断下一个分支,如过下一个判断后面对程序为空 你也可以省略else 这个关键字 例如:
if boolean_expression then
instruction;instruction; …
else
instruction;instruction; …
end
当有大于两个条件的分支时, 你可以避免过多的使用 else 语句,用多个elseif结构 代替, 例如:
if c1 then
instruction;instruction; …
elseif c 2 then
instruction;instruction; …
elseif c 3 then
instruction;instruction; …
…
else
instruction;instruction; …
end
当然这里也可以使用else 。这个比较灵活,避免了使用单一的关键字,例如:
if c1 then
instruction;instruction; …
else
if c 2 then
instruction;instruction; …
else
if c3 then
instruction;instruction; …
…
else
instruction;instruction; …
end
end
end
对于确定的表达式有很多可能的取值时,我们可以用case 结构语句来实现,根据计算表达式的值来判断执行那条语句,
这个比上面的 if 。else 更方便
面向对象的方法, 在一些特殊的多型体或者是动态邦定时,可以减少外在的条件需求和多分支的条件语句,通过支持一系列的选择,你可以应用 一个对象的某些特性,如果这个对象有很多不同的特性,则可以在程序运行时自动选择合适的 ,当程序运行时,这种方式通常是比较好的,当然如何选择结构由你 的算法决定,这个得要你自己写。
多条件分支(象我们知道的Case结构,和在Pascal语言中的关键字 ,他们都是被一个叫Tony Hoar得人设计的),在一组条件里我们可以分别的去设置一些东西,比如e = vi x 是表达式。vi是相同结构的常量,尽管条件结构(if e = v1 then …
elseif e = v2 then…) 就可以实现功能,有两个原因可以证明在一个结构里,如果你个符号可以实现的功能则不比用两个符号去实现。
这个例子大家很常见,它证明了在特殊的结构里,避免使用无用的重复的“e =?”可以提高程序的透明性。编译可以使用特殊的效率高的工具方法,跳表。但这个不适用于一般的条件结构。它可以避免外在的测试。
对于这两种不同的值来说(e和vi),多分支判断结构仅需要给出两种可能:integers 和booleans。 语法规定:e和vi必须被提前声明要么是 integers 要么就是CHARACTER。变量例如:
inspect
e
when v1 then
instruction;instruction; …
when v2 then
instruction;instruction; …
…
else
instruction;instruction; …
end
所有的vi的值不能重复,else 后面这段可有可无,每个分支的判断值必须明确给出,当然也可以没有,这个结构的作用是:如果e的值和vi值中的某一个相等(这个和case语句里有点像),执行这个值对应后面的语句,否则执行下一条if语句进行判断,如果结构里面没有else 分支,并且e的值和vi的所有值都不匹配,则结果是抛出异常(Incorrect_inspect_value),这个方法看起来很让人吃惊, 在这个相应的 条件结构以后也可以简单地让程序什么也不做,但是程序会突出显示这个特殊的分支,如果你写一组vi的值 ,至少其中要包括一个else分支,或者为空,或者没有,如果你准备外在的生命它,那么你应该知道e的值,它必须在vi的取值范围之内,可以检查这个设置或者突出显示这个设置如果不满足条件的话,现有的工具可以帮你做到这点,什么也不作将是个错误的理由,因为case语句通常有一个BUG(忘记了一种它自己的可能的case语句),这应该在所有的case语句中尽早的声明。
一种典型的多分支应用是解码单一用户的字符输入:
inspect
first_input_letter
when 'D' then
elete line”
when 'I' then
nsert line”
…
else
message ("Unrecognized command; type H for help")
end
整数的情况下,v可以可以取唯一的值、详细的概念以后章节介绍。这使得一些抽象的常数定义,例如在声明 这样,连、MI、FA、SOL、LA、SI:整形是独一无二的,那么它们之间的区别是do then 什么时候 re then 等等。 像条件结构,多分支结构不应作为替代为固定的面向对象技术,根据动态邦定。限制整数和字符,有利于避免滥用数值; 分支原理和固定值原则上将提供进一步的指导
循环的语法结构是根据目的在程序设计初期提出的,例如:
T
from
initialization_instructions
invariant
invariant
variant
variant
until
exit_condition
loop
loop_instructions
end
变量与常量的子句是可以选择的。 这些子句是必须的(但是也可以为空); 它明确设定循环的的初始值。 如果抛开选择子句,执行循环包括先执行循环标记后面的循环初始化语句 "循环过程"本身的定义如下:如果退出条件为真, 则这个循环是个空的循环;如果退出条件为假,程序性的执行完一遍循环后,在每有满足退出条件的时候接着执行下一次循环
从检查的指令也在讨论声明的范围之内。 它的作用是要求有些声明在某些方面必须满足,例如:
check
assertion -- One or more clauses
end
调试指令是一个非常灵活的条件编译工具,它是先写调试指令,再进行调试,然后结束等等。对于每个类来说,你可以打开或者关闭它相应的控制选项里面的是否调试选项。ACE文件这个文件,如果打开调试,则类中所有的调试指令对它都是同等权限的,这个也包括,如果关闭调试,程序将没有任何的运行结果。你也可以用命令去调用一些仅在调试中用到的模块,例如 你可以用指令去打印一些关注的值。
最后一个出现在讨论中的指令是重试,这个指令一般出现在调试语句里面,它的作用是重新启动被中断打断的程序主体 。
表达式的作用是表示一个计算结果的值,一个对象的或者是一个对象设计到的东西,表达式包括以下几个种类:
。显示常数。
。实体(属性、局部常用 实体,正式常用分歧 ,结果)。
。功能调用
。表达式与结果(技术上有特殊情况需要功能)。
。当前值
显示常数是自己显示自己的值(比如像整形0)--作为一个代表常量的符号,它的名字是独立于它的值的 。也就是说它的名字可以取不同的值,但都可以代表一个相同的变量。bool型的常量有两种取值分别是真或者假,整型变量的表达式取值很广泛,他们的应用简单可以表示成下面式子:
453–678+66623
实际使用小数点常数。 无论是整数还是非整数部分可以省略; 你将定义一个符号,并指定了10个整数位和的五个小数位用来表示它的值。 举例而言:
52。5 ?4。44+45。01。983?97。999。e12
字符型常量包括已经定义好的字符串In'a'; 字符常量把这个定义为一个字。 以上特征为条件,我们将图书馆定义为字符型,具体内容本章稍后讨论。
功能条用的语法需要遵循本章以前语法要求。 语法有对也有错; 在有条件的情况下,可以允许Multidot更新。 假设合适的类和功能声明如下:
bqf
bqg (x, y, …)
bqh (u, v)qiqj (x, y, …)
某些特定的功能调用原理是和一般的功能调用一样。
保留字是指目前现有的情况下,可以使用的类里面的实例,它可以被用到表达式中。note 是一个表达式而不是一个定义好的实体,所以事先定义好的实体比如:Current:= some_value将会提示语法错误。当谈到一个对象的特点(或属性或者程序)目前情况下,没有必要去写CURRENTQF。 只写F就够了。 由于这条规则,我们将用比目前较少面向对象的语言,让每一个属性都必须有明确的参考规则。 (对于Smalltalk,假如没有这个约定。 一个属性是永远正确的,即使是为适用于目前的情况下自己写) 你必须明确地说出目前实例应包括:
。目前争论的情况下通过的程序 ,象aqf(Current)。 普通的调用是创造目前实例的一个重复对象,比如x:= clone(Current)测试是否附有其他相关的对象和当前对象相关联,比如测试 x=Current。
。当前的表达式也可以用来构造复杂的表达式。
。可以在当前做标记象"anchored declaration"的形式,用来研究对象之间的继承性关系。
程序员可以构造复合的表达式。 一元的表达式有+和- ,它用于整形变量和实体变量中,但是它不适用于bool型的变量, 二元操作符,取两个要操作的数进行运算,常用符号有: =/=""=<=> 如果操作符号是"=/"则操作后的结果将是bool型的变量涉及一个或多个操作等数字变量可以用以下符号,他们可以合并使用:
+–?/^//\\
“//”是整形相除取整,“\\”是整形相除取余,“^ ”是进行幂的运算。bool型操作数可以和and ,or ,xor,then,等一起使用。最后三个操作符,将在下一部份详细讲解,xor是不可兼异或。操作符的优先级,是 根据一般数学惯例来排顺序的, 根据已制定的"最奇怪的原则"。 为了避免或不确定引起混淆,本书在有些符号的使用括号特别的标记,即使是没有必要, 具体例子将在下一节给出。
象and,then,or ,else等操作符(他们都是从Ada语言中借鉴而来的)是不可改变的 成为非严格的bool操作符,下面是它们的定义:
The operators and then and or else (whose names have been borrowed from Ada) as
非严格的bool操作
。a and then b 的结果是:如果a为假,则为假,要么就和b的直一样。
。a or else b 的结果是:如果a为真,则为真 ,要么就和b的直一样 。
。a implies b的直和 (not a) or else b一样。
从数学运算的规则上来讲bool型有两种值:真和假 真和假是确定的常量,因此它们一般写其他颜色的楷体。前两个操作符an 和or 的定义好像产生了相同的结果。 但是它们的区别是什么时候能确定B的值。 在这种情况下,利用标准数学运算符也是不能确定的,但上述定义可以产生结果:如果a是假的,a and then b的值是假的那么不管b取何值。 而如果a是真的, 不论b的值是真是假。同样,如果a是真的意味这b是假的,即使是B不能确定。 所以非严格的操作标准,可能产生的结果也不能确定。
一个典型的bool型的应用是a and then b (i /= 0) and then (j // i = k) 而从上述定义,如果是i的值为零(因为是第一次操作数 然后为假)。 如果表达式为using and rather than and then,则它的第二次操作结果将不能确定如果 i为零, 在这种情况下,并不清楚。 这种不确定性表现在什么情况在运行时间上:
B1。如果编译器产生的代码的值,然后把产生的两个值进行and运算,运算中将产生零,这在除法中是不合法的,所以会产生异常。
B2。 另一方面,如果产生的编码在第一个值为真的时候它才是真的, 换句话说如果第一个值为假则会返回一个假的值。, 然后那表达将会得到错误的的结果。
保证能编译通过B 2 ,使用and then 同样地:
(i=0)or else(j// i/= k)
将会得到真的值如果 i 是零, 然而那or实体会生产一个运行的错误。一个表达式使用and then总是产生如同and if产生的值, 但是and then可能产生的值,是错误的) 以防万一and的值不一致。 相同的结果由于or else 的 (值为真)和or。 在这种情况下, 非互相的操作可能被说是 "更多定义超过or to "它们各自的符号。 这也意谓这非严格的解释 ?策略 B 2?平常的操作数的正确执行步骤是: 一
编译器能决定用什么工具实现and as and then and or as or else。但是他不是
必须, 因此软件开发者不可能仰赖假定以便 andand 将会是非严格的; 只有and then 和 or else能保证最后两个例子的一致性。
你可能想知道为什么二个新的操作数是必须具备的; 它会不会比标准的操作数更简单和更安全,比如像andand or and take them to mean and then and andand or else, 这不改变任何 boolean型表达式的值如果两者值是事先被被定义好的, 但是会在增加一些表达式的可能值。 这的确是一些程序语言常用的方法, 特别是 ALGOLW 和 C,编译boolean 操作数。 然而有的 既是理论上的并且和实际的
保存两组截然不同的操作数:
。在理论上, 标准的数学 boolean 操作数是可以调换位置的: a and b 的值和b and a的值是相等的,但是a and then b却是b and then a 的值不一样 。 可能被定义当 b 然后一不是。 如果操作符对数值的顺序每有什么要求的话就可以互换位置
。在实际中,如果事先给定了操作符的运算顺序则一些编译器最佳化变成不可能的事
如同是情形由于那非 互相的操作数。如果两者的运算元被知道被定义 因此使用标准的操作数是比较好的,注意经过有条件的模拟非精确的操作数是可能的不包括如此的操作数。 举例来说:
b:= ((i /= 0) and then (j // i = k))
结果可能是
if i = 0 then b:= false else b:= (j // i = k) end
非严格的形式当然比较简单。当它满足条件退出循环的时候特别简单, 像是重复下列的排列:
from
i:= aqlower
invariant
-- For all elements in the interval [aqlower 。。 i –- 1], (a @ i) /= x
variant
aqupper — i
until
i > aqupper or else (a @ i = x)
loop
i:= i + 1
end;
Result:= (i <= aqupper)
上面程序的目的就是如果只有 x的值在排列出现一次,使产生的结果为在真, 那使用or会在是不正确的: 一个编译器可能产生将会产生求产生两个值得代码
所以对于最后一个索引进行检查 (i> aqupper) 如果在排列中没有值等于 x,在运行时将会有一种错误的尝试存取非存在的排列计算一 @(aqupper+1), 引起一个运行错误。(如果检查是通过则他违反了先决条件)之上, 有可能正确地非严格的找出操作数来实现这个例子, 但是那结果很繁的也不便于察看(尝试它)。
另外的一个例子是声明 - 在类不变量中举例来说明-表达完整的事物的特定目录 l 的第一价值是非零 - 已经提供,当然目录不是空的。 你可以这样做:lqempty or else lqfirst >= 0
使用or可能是不正确的。 在这里没有办法写出没有非严格的操作 (除了在声明里写一个特殊的功能函数,并调用它)。 运算法则的基础库和数据结构包含许多如此的例子。
那暗示操作,描述含意, 也是不精确的。 数学的逻辑定义 “a implies b?是 “not a or b” 但是在实际的使用 b 却是无意义的, 所以使用or else 而不是or; 这是上面官方给出的定义。 在这情况不需要一个精确的变量。
那暗示形式不总是出现在你的脑海中如果你不习惯用它, 但是它时常比较清晰; 举例来说你可能喜欢下面一个表达形式
(not lqempty) implies (lqfirst >= 0)
类里面的字符串描述的是字符型的变量活着常量。 它自从记号法以后拥有一种特别的属性,可以作为常数的载体。字符串常数在一般是用双引号括起来的件, 比如:
" ABcd Ef~*_01"
如果它出现当做字符串的开始。 双引号里面的字符串前必须有%,变量字符串也是有类string 来初始化的, string程序可以产生一定长度的字符串, 比如: text1, text2: STRING; n: INTEGER;
…
!! text1qmake (n)
动态地分配一个字符串文本 1, 预留 n字符的的存储空间。 注意 n 是开始的大小值而不是最大值; 任何的线能任意增加或者减少存储空间。字符型变量有很多属性: 串联, 个性或次线提取, 比较等等 (他们可能改变字符串的大小, 自动地
增加分配空间如果它需要的空间比实际的大。) 字符串的分配的另外一暗示: after text2:= text1, 任何的针对本文 1 的内容的修正也将会影响本文 2 的内容,相反地也成立。
如果想复制而不是共享文本2的内容可以用下面这条语句:text2:= clone (text1)。你能声明固定的字符串属性,比如:
message: STRING is "Your message here"
二个核心的提供输入输出的库的工具是:FILE and STD_文件。在对象f 上定义的操作中必须声明的类型文件是下列各项:
!! fqmake(" 名字 ") --和一个 namename 的文件关联的 f。
--为写打开 f
fqopen_write
--为读打开 f
fqopen_read
fqput_string(" A_STRING") --在 f 上写给定的字符串
为了在标准的输入/输出操作, 输出,和出错提示, 你也可以选择IO已经定义好的值, 它事先定义好了输入,输出和错误。 二者择一地你能使用和错误, 当做在 ioqput_string("ABC ") 中, 省略遗传。
标识符是字符型的的序列, 它必须是字母,数字或下划线组成; 标识符的第一个字符必须是一个字母。 对于标示符的长度没有作严格的要求,标识符的所有的属性都是是重要的。 这能使函数名和类命名尽可能清楚。
字母的大小写在标识符中不是重要的, 所以Hi, hi, HI and hI全部指示相同的标识符,原因是允许不同的两个标识符共存会是危险的。如果, Structure 和 structure表示不同的元素。 则要求开发者要为出错可能多一些考虑。然而,比较准确的记号法风格会比较好 ,这个将会在后面的章节详细说明比如: classes (INTEGER, POINT…)和在所有的大写中的叁数 (G in LIST [G]); 预先定义实体和表达式 (Result, Current…)和常数属性 (Pi) 以一个大写字母开始而且在小写中继续; 所有的其他标识符 (非常数属性, 正式的常式争论, 当地的实体) 在所有的小写中。 虽然编译器不运行他们后,他们不是记号法的规定的部份, 这些规则对于提高程序的易读性是很重要的,软件本文;类和这一本书一致地应用它们。
1。外部的程序经过一个明确的定义是很容易理解的。
2。对象技术能为软件提供一个包装机制。
3。程序不可能直接地修正他们的争论, 虽然他们可能改变那与这些争论有关的对象。
4。记号法包括一小组的内容: 任务, 有条件, 循环 , 调用, 出错, 检查。
5。表达遵循通常的用法。 当前的表达式之表示当前的值,比如:如果不作为一个实体,表达式不可能是任务的目标。
6。当两者的运算元被定义,非精确的 boolean 操作产生如同标准的 boolean 操作员一般的值, 但是在有些时候则不是这样 。
7。字符, 输入和输出被简单基础类包括。
8。字符情形在标识符中不是重要的,虽然我们推荐一些常用的书写风格,你也可以自己定义。
该如何整合的讨论外的软件,虽然软件的特点是为非 O-O,并且它里面的变量都是正确的, 和另外一-定向语言可能在类中发生。 讨论"外部的类"的概念就是为那个目的,和本书的记号法一致。
如果元素 x 在排列出现,定义一个循环, 类似运算法则参考这一个章节,但是不使用任何一个不正确的操作符。