PL真有意思(四):控制流 (2)

除了可以指定默认值之外,还可以采用另外一种方式,将对为初始化的变量的使用作为动态语义错误,在运行时捕获这种错误。但是在运行时捕获所有使用到未初始化的情况的代价非常高

定义性赋值

在Java和C#中提供了一种定义性赋值的表示形式,意思就是由编译器来检查在达到一个表达式的所有可能控制路径上,都必须为这个表达式中的每个变量赋过值

构造函数

许多面向对象语言都提供了为用户定义的类型的自动初始化方法,也就是构造函数

在C++中,还区分了初始化和赋值,它将初始化赋值解释为调用变量所属类型的构造函数,以初始值作为调用参数。在没有强制的情况下,赋值被解释为调用相关类型的赋值运算符,如果没有定义赋值运算符,就默认将赋值右部的值简单的按位复制过来

区分初始化和赋值的好处是,可以区分在赋值前是不是需要先释放空间

表达式的顺序问题

虽然优先级和结合性规则定义了表达式里二元中缀运算符的应用顺序,但却没有明确说明特定运算符的各运算对象的求值顺序。举例来说,如下表达式:

a - f(b) - c * d

根据结合性可知a-f(b)将在第二个减法前执行,根据优先级可知第二个减法的右运算对象是cd这个整体而不是c。但是如果没有进一步的规则描述,我们无法得知a-f(b)是否在cd之前运行。诸如此类:对于f(a,g(b),c)这个子程序调用,我们也不知这三个参数的求值顺序。

求值顺序之所以重要:

副作用:如果f(b)这个子程序可能会修改c的值,那么a-f(b)-cd的求值结果将依赖f(b)和cd哪一个先执行;类似的,如果g(b)修改了a或者c的值,那么f(a,g(b),c)的结果也是依赖于参数的求值顺序。

代码改进:子表达式的求值顺序对于寄存器分配和指令调度都有重要的影响。比如(ab+f(c)),我们可能会希望在执行ab之前调用f(c)。因为如果先计算乘法,则在调用f(c)之前就要先保存起来乘积,因为f(c)可能会用光所有的寄存器。

短路求值

对于布尔表达式,如果编译器可以对其执行短路求值,那么它生成的代码可以在表达式前一半的结果可以确定整个表达式的值的情况下跳过后一半的计算。

比如(a<b) and(b<c),如果a>b,那么完全没必要去检查b是否小于c就可以确定这个表达式一定为假。在一些特殊情况下,短路求值可节省大量时间,比如if(func&&func())。实际上这种情况下短路求值已经改变了布尔表达式的语义,如果非短路求值,那么在func不存在的情况下去执行func(),程序是会抛出错误的。

我们常见的语法表现形式是&&和||这种布尔运算符身兼多职,既是布尔运算符又会触发短路求值,但是有一些语言针对短路求值是有单独的语法形式的,比如Clu语言中布尔运算符是and和or,短路运算符是cand和cor。这是为何呢,因为有些代码逻辑是不需要这种短路求值的优化的。

结构化和非结构化的流程

汇编语言中的控制流通过有条件的或无条件的跳转(分支)指令来完成,早期的高级语言模仿这种方式(如Fortran),主要依赖goto来描述大部分非过程化控制流,比如下面代码:

if A < B goto label1; label1;

但是如今goto像在Java、Clu和Eiffel里已经完全被禁止了,在其它语言也是受限了或者只是为了向前兼容而已

goto的结构化替代品

对于goto被废弃,各种使用goto的地方也被结构的方案给代替了

循环中的退出和继续

break和contiune这两个关键字大家应该很熟悉了

从子程序提前返回

return

多层返回

上面的两个问题都可以有很好的替代品,但是对于多层返回就会比较麻烦一点。return或”局部的goto“只能在子程序中返回,如果遇到多层嵌套的子程序,想从内层的子程序返回来结束外围子程序的执行,那return和局部的goto就无能为力了。这种情况下,语言实现要保证能恰当的恢复栈上的子程序调用信息,这种修复工作称为"回卷",为完成此事,不仅必须释放需要跳出的所有子程序的栈帧,还要执行一些信息管理工作,比如恢复寄存器内容。

Common Lisp提供了return-from语句来明确指定需要退出的词法外围函数或嵌套块,还可以提供一个返回值:

Common Lisp和另外一个语言Ruby中还内置一个throw/catch语法来支持这种多层返回,注意这种结构并不是所谓的异常处理,而是一种多层返回的语法结构,直白点说是一种功能强大的变相”goto“,看下面代码:

//定义一个方法 def search_file(filename,pattern) file=File.Open(filename) //遍历文件每一行 file.each{|line| //根据parrern匹配模式查找,如果匹配就返回到定义found标签的位置 throw :found,line if line=~/#{pattern}/ } end //用catch定义一个found标签 math=catch:found do serach_file("f1",key) serach_file("f2",key) //如果f2文件找到了则就会返回line至math serach_file("f3",key) ”not fount“ //找不到就执行到此处了 end print match

错误和异常

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfgwf.html