在Ada中的常量声明

6

作为一个Ada的新手,我正在研究其语法和规则,并希望引起对下面给出的代码的关注。这里我试图设置一个变量Actual_Stiffness来保存一个常量值。它的值由以下乘积给出:

Actual_Stiffness := Stiffness_Ratio * Stiffness_Total

Stiffness_Total在规范文件Material_Data.ads中被定义为一个常量Long_Float,并且在ads文件中已经设置了它的值。

WITH Ada.Text_IO;
WITH Ada.Long_Float_Text_IO;
WITH Material_Data;
USE Material_Data;

PROCEDURE sample IS

   Stiffness_Ratio  : Long_Float;
   Actual_Stiffness : CONSTANT Long_Float :=  Stiffness_Ratio * Stiffness_Total;

BEGIN -- main program
   Ada.Text_IO.Put("Enter stiffness ratio: ");
   Ada.Long_Float_Text_IO.Get(Item => Stiffness_Ratio);
   Ada.Long_Float_Text_IO.Put(Item => Stiffness_Ratio);

   --Ada.Text_IO.New_Line;
   --Ada.Long_Float_Text_IO.Put(Item => Actual_Stiffness);
   --Ada.Text_IO.New_Line;
   --Ada.Long_Float_Text_IO.Put(Item => Stiffness_Total);
END sample;

编译时出现警告信息:

警告: “Stiffness_Ratio”可能在赋值前被引用

在运行程序时,Actual_Stiffness的值不正确。我可以将Actual_Stiffness定义为仅为Long_Float(而不添加CONSTANT),然后在我的程序中,在BEGIN之后从产品Actual_Stiffness:= Stiffness_Ratio * Stiffness_Total中获取其值,此时Stiffness_Ratio已经获得了一个值。这是正确的做法。

我的问题是:

我已将Stiffness_Total定义为具有预定值的常量Long_Float。如何定义Actual_Stiffness也为常量(因为它不会在程序中更改),同时保持用户能够交互地在终端输入Stiffness_Ratio的能力?是否可能实现这一点?

非常感谢。

2个回答

3
由于“Stiffness_Ratio”在运行时才确定,编译器无法像你要求的那样在编译时计算“Actual_Stiffness”的值。您需要将“Actual_Stiffness”变成非常量变量,并在“Stiffness_Ratio”有值后使用计算初始化它。(只要在计算实际刚度时刚度比率有值,您甚至可以将其保留为函数中的常量。)这在大多数编程语言中都是标准的。因此,回答您的问题:不,您不能按照所需定义Actual_Stiffness作为常量。一个相对接近的近似值是,您在该过程之外确定Stiffness_Ratio值,并将该值作为参数传递到该过程中。但是,然后Actual_Stiffness仅在该过程的持续时间内是常量,而不是一直如此。另一方面,这可能更有用;某人可以在不同时间以多个刚度比率值运行程序,从而在单次运行中进行许多模拟。

谢谢。这是正确的做法。我猜你的意思是“你将不得不制造实际刚度...”。 - yCalleecharan
@yCalleecharan:是的 - 我的错误。我会修复的。 - Jonathan Leffler

3
在Ada语言中,完全可以并且是合理的在函数中间声明变量。就像其他编程语言(至少包括Java、C、C++、C#和Python)一样,你可以在任何地方显式地创建作用域:
declare
   A : My_Type := Val;
begin
   Use(A);
end;

这意味着您的常量可以在接收运行时值后声明:
WITH Ada.Text_IO;
WITH Ada.Long_Float_Text_IO;
WITH Material_Data;
USE Material_Data;

PROCEDURE sample IS

   Stiffness_Ratio  : Long_Float;

BEGIN -- main program
   Ada.Text_IO.Put("Enter stiffness ratio: ");
   Ada.Long_Float_Text_IO.Get(Item => Stiffness_Ratio);
   Ada.Long_Float_Text_IO.Put(Item => Stiffness_Ratio);

   --Ada.Text_IO.New_Line;
   declare
      Actual_Stiffness : CONSTANT Long_Float :=  Stiffness_Ratio * Stiffness_Total;
   begin
      Ada.Long_Float_Text_IO.Put(Item => Actual_Stiffness);
   end;
   --Ada.Text_IO.New_Line;
   --Ada.Long_Float_Text_IO.Put(Item => Stiffness_Total);
END sample;

例如,考虑以下欧几里得最大公约数算法的非递归实现:
   function GCD(X,Y : Integer) return Integer is
   begin
      declare
         X : Integer := GCD.X;
         Y : Integer := GCD.Y;
      begin
         while Y /= 0 loop
            declare
               temp : Integer := X;
            begin
               X := Y;
               Y := temp mod Y;
            end;
         end loop;
         return X;
      end;
   end GCD;

在GCD中,参数X和Y始终是常量,因为它们被声明为in。这是为了避免混淆;如果允许对变量进行赋值,程序员可能会认为他正在引起副作用,就像参数标记为out的情况一样。
约翰·巴恩斯(John Barnes)在《Ada 2012编程》中建议在函数本身的范围内使用不同名称声明一个变量。第194页,第11.2节。
   function Sum(List: Cell_Ptr) return Integer is
      Local: Cell_Ptr := List;
      (...)

这使得程序员可以引用List,即常量和不变的初始值。在上面给出的GCD程序中,在循环体中引用该值会导致错误行为。
通过添加具有相同名称变量的内部作用域来隐藏X和Y,这意味着我们需要明确包含变量的作用域才能犯这个错误。尝试将X:= Y更改为X:= GCD.Y在循环体中,并观察GCD(91,21)现在返回21而不是7。
最后,请注意可以通过添加标签来给内部作用域命名:
   Inner:
   declare
      X : Integer := 0;
   begin
      Use(Inner.X); -- explicitly the X in that scope
   end Inner;

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