子程序:过程和函数 - 1


子程序是程序的一个独立部分,它有一个名字并解决它自己的独立任务。子程序位于主程序的开头,可以通过指定名称从主程序启动(调用)

使用子例程可以避免代码重复,以防您需要在程序的不同位置编写相同的代码。 
导入到程序中的库(例如,数学库 math)包含已经由某人编译的子例程。程序员不需要考虑他们实现了什么算法,而只是简单地应用它们,只考虑他们到底在做什么。这是一个很大的节省时间。没有必要去写别人已经写好的算法。

每个例程 应该只做一件事: 要么只是计算一些东西,要么输出一些数据,要么做其他事情。 

有两种类型的子例程 - 过程函数

子例程执行一些操作,例如以某种形式将结果打印到屏幕(一个简单的例子,声明 writeln() 是一个标准的子例程,打印到屏幕信息)

函数子程序返回一个结果(数字、字符串等),我们可以在主程序中使用。

让我们尝试编写一个简单的程序:
假设我们需要在屏幕上显示字符串“Error”每次由于用户的错误(例如,当他输入不正确的数据)而导致代码中出现错误时
这可以通过编写语句来完成 <前> writeln('错误'); 而现在想象一下,在程序的很多地方都需要插入这样一行。当然,你可以到处写。但是这个解决方案有两个缺点。
1)这个字符串会多次存入内存
2) 如果我们想改变错误的输出,我们将不得不在整个程序中改变这一行,这是相当不方便的

对于这种情况,需要程序。
带有过程的程序可能如下所示: <前> ... 使用命名空间标准; 程序打印错误(); // 过程描述 开始 writeln(“错误”); // 过程主体 - 过程将执行的命令 结尾; // 主程序 开始; ... 打印错误(); // 开始执行过程。我们只需指定要执行的过程的名称。 ... 打印错误(); ... 结尾。 该过程以单词procedure开头。 在过程名称后写有括号,表示过程执行所依赖的变量及其类型。例如:

var a, b, answer: integer;
程序 Sum(a, b: integer);
开始
   答案:= a + b;
结束;

在过程中执行的所有语句都是缩进的。 

程序写在主程序之前

要执行一个过程,您需要在主程序中调用它按名称并记住写括号
您可以多次调用程序中的过程。

现在让我们想象一下,我们需要显示不同的消息来响应用户的错误,这取决于他犯了什么样的错误。
在这种情况下,您可以为每个错误编写自己的过程:   <前> 程序 printErrorZero(); 开始 writeln('错误。被零除!'); 结尾; <前> 程序 printErrorInput(); 开始 writeln('输入错误!'); 结尾; 如果有更多可能的错误怎么办?这个解决方案不适合我们!
我们需要学习如何通过告诉程序显示什么错误信息来控制程序。
为此,我们需要将参数写在过程名称后的括号中 <前> 程序 printError(s: 字符串); 开始 writeln(s); 结束; 在此过程中,s 是一个参数 - 一个允许您控制过程的特殊变量。
参数是一个变量,决定子程序如何工作。参数名称在子程序头中以分号分隔列出。在参数之后,冒号 后跟它的类型。

现在,在调用该过程时,您需要在括号中指明将分配给我们过程中的参数(变量 s)的实际值 <前> printError('错误!除以零!'); 该值称为参数。
参数是调用子程序时传递给子程序的参数值。
参数不仅可以是常量值,还可以是变量或算术表达式。

通常需要使用仅在子程序中使用的附加变量。此类变量称为本地(或本地),只能在创建它们的子例程内进行操作。
 
Local variable scope 是声明它的函数或过程

因此,可以将变量的作用域(scope)限制在真正需要它的子程序中。在编程中,这种技术称为封装  - 隐藏变量不被外部更改。

如果有必要声明一个在程序中任何地方(在任何子程序中)都可见的变量,则此类变量在所有子程序之外声明(参见下表中的程序 3)
此类变量称为全局

分析三个程序: <正文> 显示在屏幕上

1) 在这个程序中,变量 i 是局部变量。在子程序中声明局部变量 2) 这里即使主程序中有变量i(值为7),也会新建一个值为5的局部变量i。 
当你运行这个程序时,屏幕会显示值75
3) 这个程序有一个全局变量i。它的值可以在子程序和主程序中改变
该过程将使用全局变量 i 并为其分配一个等于 2 的新值。值 2
程序测试();
var i: 整数;
开始
   我 := 5;
    writeln(i);
结束;
变量 i: 整数;

程序测试();
var i: 整数;
开始
   我 := 5;
    writeln(i);
结束;

开始
   我 := 7;
   写(我);
   测试();
结束。
变量 i: 整数;

程序测试();
开始
   我 := 2;
结束;

开始
   测试();
    writeln(i);
结束。
问题:编写一个交换两个变量值的程序。
此任务的特点是我们需要在过程中所做的更改才能为调用程序所知。

让我们尝试编写这样的程序:

var x, y: 整数; <前> 程序交换(a,b:整数); // 有了这样的过程参数描述, 变量 c:整数; begin // 将复制参数(x 和 y)的值 // 变量 a 和 b 是与 x 和 y 无关的自变量 c := 一个; 一个:=乙; b := c; 结尾; 开始 x := 1;   y := 2; 交换(x,y); //变量x和y(参数)的值被复制到参数a和b中 writeln('x = ', x, ', y = ', y); // x=1, y=2 结尾。 如果你运行这个程序,你可以看到变量xy的值没有改变。为了使参数改变参数的值,您必须使用通过引用传递数据。 为此,在数据类型的名称之后在子例程的标题中,您必须输入单词 var  <前> 过程 Swap(var a, b: integer);   // 现在变量 a 和 b 得到变量 x 和 y 在内存中的地址 var c: 整数;  开始 c := 一个; 一个:=乙; b := c; 结尾; 用法:如果您通过引用传递参数,那么在调用过程时只有变量名(不是数字也不是算术表达式)可以出现在这个地方!< br />
不要调用这样的过程: <前> 交换(x,4); 交换(5+x, y);