如何在Pascal中的一个过程内调用另一个过程

6
procedure questiontype;  
 begin  
  writeln ('Enter the type of question you would like...');  
  writeln ('1. Add');  
  writeln ('2. Multiply');  
  writeln ('3. Subtraction');  
  writeln ('4. Division');  
  readln (typeofquestion);  
   case typeofquestion of
    1: add;
    2: multiply;
    3: subraction;
    4: division   
else writeln ('Choose again'); 
end;
end;          

加、乘、减和除法都是过程。如果我将它们放在主程序中,它们将正常运行,但当我将它们作为单独的过程时,我会收到“未声明的标识符”错误。我已经查看了许多网站,寻找类似于此的示例,但我找不到任何内容。

如何从这个程序内部调用加、乘、减、除法过程?


你不会使用 goto 命令来调用一个过程,而是使用 call 或者 invoke 命令。这两者之间的区别看起来可能微不足道,但是 Delphi 有一个 goto 关键字,你不应该使用它。甚至你现在也不需要去了解它。 - Cosmin Prund
@Cosmin,@David:纯Pascal中没有case语句吗? - Andreas Rejbrand
@Andreas Pascal有一个与Delphi相同的case语句。 - David Heffernan
@Cosmin goto 在 Pascal 中可能很有用,因为它没有异常。 - David Heffernan
@Cosmin 我有一种感觉,原始的Pascal也没有break或exit,这使得goto更具吸引力!但我不会对这个说法冒险。 - David Heffernan
显示剩余8条评论
3个回答

14

在调用过程之前,必须先声明要使用的过程。虽然您没有展示其他程序的定义方式,但我推断它们是在您展示的程序之后定义的。

因此,您可以简单地重新排列代码,使加法、乘法、减法和除法在调用它们的过程之前定义。

所以这样做会起作用:

procedure add;
begin
  //do something;
end;

procedure questiontype;  
begin  
  add;  
end;

但是这段代码无法编译:

procedure questiontype;  
begin  
  add;  
end;

procedure add;
begin
  //do something;
end;

Pascal及其变种语言是一遍编译的,如果编译器在提到某个子程序时不知道它的存在,编译就无法继续。

Pascal支持协程,可以通过使用“前向声明”实现A调用B并且B调用A。例如:

procedure B; forward;

procedure A;
begin
  B;
end;

procedure B;
begin
  A;
end;

很明显,按照现在的写法这是一个无限循环,并最终导致堆栈溢出(多么恰当啊)。但是当然有真正需要这种写法的情况。

然而,前向声明很少需要且应该尽可能避免使用,因为它们会增加复杂度。通常可以通过重新排列声明来找到解决方案。

最后一句话,声明必须在使用之前发生的排序约束在Brian Kernighan著名文章《为什么Pascal不是我的最爱编程语言》中有明确提到。


@David,你最近几天收到了多少个踩的赞数?比平常多很多吗?在我看来,这似乎是一种流行病。自从我开通SO账户以来,我总共收到了8个踩的赞数,其中4个是在过去的4天内。我的数字在统计上是不相关的,它们太小了。 - Cosmin Prund
2
我的猜测是那个投票者只看了问题的第一个标签,即 [delphi],然后注意到你没有提到 interface 部分。或者也许是那个投票者心情不好。但是,我认为你永远不应该在不留下评论的情况下投反对票。 - Andreas Rejbrand
@Cosmin:几周前,有人(显然)给我过去一周左右的每个答案都投了反对票。 - Andreas Rejbrand
ISO 7185没有单位。后来的标准(10206)具有模块系统,但其语法更类似于Modula2,其中iirc MODULE DEFINITION和IMPLEMENTATION分别替代了UNIT / INTERFACE / IMPLEMENTATION关键字。 TP派生的UCSD模型据我所知是基于10206的早期概念草案,但语法后来发生了变化。 - Marco van de Voort
小心使用“协程”这个术语。通常情况下,该术语意味着不同的东西。 - Rudy Velthuis
显示剩余2条评论

6
我看到您在标记问题时使用了[delphi][pascal],因此我猜测您实际上正在编写Delphi代码。除了关心David所讨论的过程顺序和forward指令之外,您还有几个选项。

大多数情况下,Delphi项目(GUI或控制台)被划分为“单元”。典型的单元如下所示:

unit MyUnit;

interface

const
  RANDOM_NUMBER = 17;

var
  PrintExtraNiceMessage: boolean;

procedure DoThis;
procedure DoThat;

implementation

const
  BUFFER_SIZE = 256;

procedure InitSomething;
begin
  // TODO: do some internal work...
end;

procedure DoThis;
begin
  // TODO: do something
end;

procedure DoThat;
begin
  // TODO: do something else
end;

您会发现这个单元被分成了两个部分: interface 部分和 implementation 部分。 interface 部分仅包含声明(函数、过程、类型、常量和变量);在此处声明的函数和过程在 implementation 部分中定义(即实现)。请注意,implementation 部分中可能有没有在 interface 部分中声明的函数和过程。
这个伟大的想法是,interface 部分的内容对程序中的所有其他单元都可见,而 implementation 部分的内容只在该单元内部可见。因此,程序中的任何其他单元都可以使用 RANDOM_NUMBER 常量、PrintExtraNiceMessage 变量以及 DoThisDoThat 两个过程。但是,你只能在这个单元内部使用 InitFunction(例如,在 DoThisDoThat 中)。此外,常量 BUFFER_SIZE 也不会在该单元之外可见。
这是一种非常优雅的方法。 interface 部分描述了如何在其他单元中使用该单元(例如,有哪些函数以及它们如何使用),而实现细节则“隐藏”在 implementation 部分中。
这种方法的好处是,它至少可能解决了您的问题。如果 addmultiplysubtractdivide 过程应该对其他单元可见,则它们应在 interface 部分中声明。但是,如果它们不应被其他单元使用,那么它们就不应在 interface 部分中声明,并且您需要像 David 建议的那样做。如果您的项目中根本没有常规单元,即如果您只有 program 文件,则也适用此规则,该文件没有划分为 interfaceimplementation 部分。

小细节,我并没有主张 OP 用 forward 来解决问题。 - David Heffernan

2
请注意,OP的示例有一个else只适用于最后一个“if”。假设如果他们输入1、2或3,相应的过程会触发,然后返回,并且他们会看到“再次选择”。如果输入4,则不会。这可以通过Case或级联if..else if结构很好地解决,其中最终的else仅在“所有其他情况失败时”才会触发,这就像OP所想的那样。

是的,这是OP代码中的一个错误。他应该在这里真正地使用case语句(或者更多地了解ifelse的工作原理)。 - Andreas Rejbrand
请在您的回答中说明这两种方法。 - Andreas Rejbrand
这有点尴尬。我现在加了一个case语句,它运行得很好。谢谢。 - captiv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接