-
流程控制 编辑
控制流程(也称为流程控制)是计算机运算领域的用语,意指在程序运行时,个别的指令(或是陈述、子程序)运行或求值的顺序。不论是在声明式编程语言或是函数编程语言中,都有类似的概念。
中文名:流程控制
别名:控制流程
类别:计算机运算领域的用语
释义:个别的指令运行或求值的顺序
不同的编程语言所提供的流程控制指令也会随之不同,但一般可以分为以下四种:
继续运行位在不同位置的一段指令(无条件分支指令)。
若特定条件成立时,运行一段指令,例如C语言的switch指令,是一种有条件分支指令。
运行一段指令若干次,直到特定条件成立为止,例如C语言的for指令,仍然可视为一种有条件分支指令。
运行位于不同位置的一段指令,但完成后会继续运行原来要运行的指令,包括子程序、协程(coroutine)及延续性(continuation)。
停止程序,不运行任何指令(无条件的终止)。
中断以及Unix系统中的信号等较低级的机制也可以造成类似子程序的效果,不过通常这类机制会用来回应外部的事件或是输入。程序自修改因为其对代码的影响,也会影响控制流程,但多半不会有明显的流程控制指令。
在机器语言或汇编语言中,流程控制是借由修改程序计数器数值来达到。一些中央处理器只支持条件分支(branch)或是无条件分支(有时会称为jump)。
标记
标记是一个标示在源代码固定位置中的名称或数字,其他位置的流程控制指令可以参考标记的位置,运行标记位置所对应的程序。标记本身不影响程序的进行,除了标示位置外,对程序运行没有其他的作用。
有一些编程语言(像Fortran及BASIC等)利用行号作为标记。行号是标示在每一行程序最前面的自然数,不一定要是连续的数字,在不受流程控制指令影响的情形下,程序会从最小的行号依序运行,而流程控制指令需指定对应的行号。以下是一个BASIC的例子:
LETX=3 PRINT"*" LETX=X-1 IFX>0THENGOTO20 END
在像是C及Ada等编程语言中,标记是一个标识符,一般出现在一行的最前面,后面会加一个冒号作为识别,以下是C语言的例子:
Success: printf ("The operation was successful.\n");
Algol 60语言同时支持整数(类似行号)及标识符的标记(二者后面都要加上冒号),不过其他Algol语言几乎都不支持整数的标记。
Goto
goto 指令(来自英文go和to的组合)是无条件流程控制指令中最基本的型式。一般在程序中会用以下的方式出现(指令大小写可能会依编程语言而不同)
gotolabel
goto 指令的效果是调整程序的控制流程,后续就运行标记位置的程序。
goto 指令是许多的计算机科学家视为有害(considered harmful)的指令,例如Edsger Wybe Dijkstra提出了goto有害论。
子程序
子程序(subroutine)可以用许多不同的术语来表示,例如程序、函数(尤其是有传回值时)或是方法(特别是子程序属于一个类的一部份)等。
子程序是是完成一项特定工作的代码串行,其他程序可以将流程移转到子程序中,运行特定工作后再回到原来的程序,若程序中有许多部份都需要运行一特定工作,利用子程序的方式可以利用一段程序达到上述的功能,可以减少代码的长度。
如今子程序也常用来使得程序更加的结构化,例如可以将一些特殊的算法或特殊的数据访问方式放在子程序中,和其他代码隔离。子程序也是程序模块的一种,若许多程序员共同开发一个程序,子程序也有助于其工作的分区及分工。
其他的研究说明若控制结构只有一个进入点(entry)及一个退出点(exit),这样的程序会比其他型式的程序容易理解。因此这样的程序可以像一个指令一样放在程序的任何部份,不必担心会破坏其结构,换句话说,这种程序是“可组成的”(composable)。
没有特定关键字的语言:Algol 60、C、C++、Haskell、Java、Pascal、Perl、PHP、PL/I、Python、Windows PowerShell。这类语言需要有关键字可以将group程序指令together:
Algol 60及 Pascal:begin ... end
C, C++, Java, Perl, PHP, and PowerShell:利用大括号{ ... }
PL/1:DO ... END
Python:利用缩进(indentation)的层次,详细内容请参考Off-side规则
Haskell:可以利用缩进或大括号,两者可以混用
有特定关键字的语言:Ada、Algol 68、 Modula-2(Modula-2)、Fortran 77、Visual Basic,使用的特定关键字依编程语言而不同:
Ada: 其关键字为 end + space + 启始控制结构的关键字,如if ... end if, loop ... end loop
Algol 68, Mythryl:将启始关键字反写,如if ... fi, case ... esac
Fortran 77: 其关键字为 end + initial keyword,如IF ... ENDIF, DO ... ENDDO
Modula-2: 不论何种控制结构,其关键字均为END
Visual Basic: 每种控制结构均有各自的结尾关键字,如If ... End If; For ... Next; Do ... Loop
在一些函数编程语言(例如Haskell和Scheme)中会使用递归或不动点组合子来达到循环的效果,其中尾部递归是一种特别的递归,很容易转换为迭代。
条件处理
PL/I编程语言中有22种标准的条件(如 ZERODIVIDE SUBSCRIPTRANGE ENDFILE),可以在程序中设置,当特定条件成立时需进行的指令,程序设计者也可以定义自己的条件,并在程序中使用。
条件成立时,只能设置一个需进行的指令(类似未结构化的if指令),大部份的应用中,都会指定运行goto指令,跳到其他代码运行对应的流程。
不过因为有些条件处理的实现会增加许多代码及运行时间(特别SUBSCRIPTRANGE),所以许多程序设计者会尽量不使用条件处理。
条件处理的语法如下:
ON 条件 GOTO label
异常处理
有些编程语言可提供不需要使用GOTO的结构化异常处理程序:
try{xxx1//Somewhereinherexxx2//use:'''throw'''someValue;xxx3}catch(someClass&someId){//catchvalueofsomeClassactionForSomeClass}catch(someType&anotherId){//catchvalueofsomeTypeactionForSomeType}catch(...){//catchanythingnotalreadycaughtactionForAnythingElse}
在try{...}的区块中,若 有异常情形时,程序就会离开try的区块,由后续的一个或多个catch子句判断需运行何种异常处理。在D、Java、C#及Python编程语言 中,try{...}区块中还可以加入一个finally子句,不管程序流程是否离开try{...}区块,finally子句中的程序都一定会运行,常 用在当程序退出处理时,需要放弃一些外部资源(文件或数据库链接)的情形下:
FileStreamstm=null;//C#exampletry{stm=newFileStream("logfile.txt",FileMode.Create);returnProcessStuff(stm);//maythrowanexception}finally{if(stm!=null)stm.Close();}
由于上述情形相当普遍,C#提供一种特殊的语法进行相同的处理:
using(FileStreamstm=newFileStream("logfile.txt",FileMode.Create)){returnProcessStuff(stm);//maythrowanexception}
只要离开 using区块,编译器会自动释放stm对象,Python的 with指令及也有类似的功能。
这些语言都有定义标准的异常情形及其出现的条件,程序设计者也可以丢出自己产生的异常(其实C++及Python的throw和catch支持绝大多数形态的对象)。
若某一个throw指令找不到对应的catch,控制流程会离开目前的副程序或控制结构,设法找到对应的catch,若到主程序的结尾还是找不到对应的catch,程序会强制退出,并显示适当的错误信息。
AppleScript脚本语言可以将"try"区块分为几个部份,提供不同的信息及异常:
trysetmyNumbertomyNumber/0onerrorenumbernfromftotpartialresultprif(e="Can'tdividebyzero")thendisplaydialog"Youidiot!"endtry
延续性
延续性(Continuation)可以将目前子程序的运行状态(包括目前的堆栈,区部变量及运行到的位置)存储成一个对象,后续在其他子程序中可以利用此对象回到此子程序现在的运行状态。
延续性一词也可以指第一类延续性(first-class continuation),是指编程语言可以在任意时间点存储目前的运行状态,并在之后回到之前存储的运行状态。
程序需要分配空间给子程序用到的区域变量,而且在子程序退出时需发布这些变量用到的空间。许多编程语言利用调用堆栈来存放这些变量,可以简单快速的分配及发布空间。也有一些编程语言使用动态存储器分配来存储变量,可以较灵活的分配变量,但分配及发布空间较不方便。这二个架构下延续性的处理方式也会不同,各有其优点及缺点。
Scheme语言利用call-with-current-continuation函数(缩写为call/cc) 可提供延续性功能。
1、本站所有文本、信息、视频文件等,仅代表本站观点或作者本人观点,请网友谨慎参考使用。
2、本站信息均为作者提供和网友推荐收集整理而来,仅供学习和研究使用。
3、对任何由于使用本站内容而引起的诉讼、纠纷,本站不承担任何责任。
4、如有侵犯你版权的,请来信(邮箱:baike52199@gmail.com)指出,核实后,本站将立即删除。