跳转到内容

Ada 编程/数学计算

来自 Wikibooks,开放的书籍,开放的世界
这篇文章是关于计算机编程的,它提供了 伪代码Ada

Ada. Time-tested, safe and secure.
Ada。经久考验,安全可靠。

Ada非常适合各种计算。你可以定义自己的定点和浮点类型,并且借助泛型包调用所有需要的数学函数。在这方面,Ada 与 Fortran 相当。本模块将向你展示如何使用它们,同时我们将创建一个简单的 RPN 计算器。

简单计算

[编辑 | 编辑源代码]

加法可以使用预定义的运算符 + 完成。该运算符为所有数字类型预定义,以下工作代码演示了它的使用

文件: numeric_1.adb (查看, 纯文本, 下载页面, 浏览所有)
-- A.10.1: The Package Text_IO [Annotated]
with Ada.Text_IO;

procedure Numeric_1 is
   type Value_Type is digits 12
         range -999_999_999_999.0e999 .. 999_999_999_999.0e999;

   package T_IO renames Ada.Text_IO;
   package F_IO is new  Ada.Text_IO.Float_IO (Value_Type);

   Value_1 : Value_Type;
   Value_2 : Value_Type;

begin
   T_IO.Put ("First Value : ");
   F_IO.Get (Value_1);
   T_IO.Put ("Second Value : ");
   F_IO.Get (Value_2);

   F_IO.Put (Value_1);
   T_IO.Put (" + ");
   F_IO.Put (Value_2);
   T_IO.Put (" = ");
   F_IO.Put (Value_1 + Value_2);
end Numeric_1;

减法可以使用预定义的运算符 - 完成。以下扩展演示展示了 + 和 - 运算符的联合使用

文件: numeric_2.adb (查看, 纯文本, 下载页面, 浏览所有)
-- A.10.1: The Package Text_IO [Annotated]
with Ada.Text_IO;

procedure Numeric_2
is
  type Value_Type
  is digits
     12
  range
     -999_999_999_999.0e999 .. 999_999_999_999.0e999;

  package T_IO renames Ada.Text_IO;
  package F_IO is new  Ada.Text_IO.Float_IO (Value_Type);

  Value_1   : Value_Type;
  Value_2   : Value_Type;
  Result    : Value_Type;
  Operation : Character;

begin
  T_IO.Put ("First Value  : ");
  F_IO.Get (Value_1);
  T_IO.Put ("Second Value : ");
  F_IO.Get (Value_2);
  T_IO.Put ("Operation    : ");
  T_IO.Get (Operation);

  case Operation is
     when '+' =>
        Result := Value_1 + Value_2;
     when '-' =>
        Result := Value_1 - Value_2;
     when others =>
        T_IO.Put_Line ("Illegal Operation.");
        goto Exit_Numeric_2;
  end case;

  F_IO.Put (Value_1);
  T_IO.Put (" ");
  T_IO.Put (Operation);
  T_IO.Put (" ");
  F_IO.Put (Value_2);
  T_IO.Put (" = ");
  F_IO.Put (Result);

<<Exit_Numeric_2>>
  return;

end Numeric_2;

纯粹主义者可能会对使用 goto 感到惊讶,但有些人更喜欢在函数内部使用 goto 而不是使用多个 return 语句,因为人们对这个问题的看法差异很大。请参阅 goto 不是邪恶的 文章。

乘法可以使用预定义的运算符 * 完成。有关演示,请参阅下一章关于除法的部分。

除法可以使用预定义的运算符 /modrem 完成。运算符 / 执行普通除法,mod 返回模除法,rem 返回模除法的余数。

以下扩展演示展示了 +-*/ 运算符的联合使用,以及使用四数宽堆栈存储中间结果

运算符 modrem 不在演示中,因为它们只对整数类型定义。

文件: numeric_3.adb (查看, 纯文本, 下载页面, 浏览所有)
with Ada.Text_IO;

procedure Numeric_3 is
   procedure Pop_Value;
   procedure Push_Value;

   type Value_Type is digits 12 range
     -999_999_999_999.0e999 .. 999_999_999_999.0e999;

   type Value_Array is array (Natural range 1 .. 4) of Value_Type;

   package T_IO renames Ada.Text_IO;
   package F_IO is new Ada.Text_IO.Float_IO (Value_Type);

   Values    : Value_Array := (others => 0.0);
   Operation : String (1 .. 40);
   Last      : Natural;

   procedure Pop_Value is
   begin
      Values (Values'First + 1 .. Values'Last) :=
        Values (Values'First + 2 .. Values'Last) & 0.0;
   end Pop_Value;

   procedure Push_Value is
   begin
      Values (Values'First + 1 .. Values'Last) :=
        Values (Values'First .. Values'Last - 1);
   end Push_Value;

begin
   Main_Loop:
   loop
      T_IO.Put (">");
      T_IO.Get_Line (Operation, Last);

      if Last = 1 and then Operation (1) = '+' then
         Values (1) := Values (1) + Values (2);
         Pop_Value;
      elsif Last = 1 and then Operation (1) = '-' then
         Values (1) := Values (1) + Values (2);
         Pop_Value;
      elsif Last = 1 and then Operation (1) = '*' then
         Values (1) := Values (1) * Values (2);
         Pop_Value;
      elsif Last = 1 and then Operation (1) = '/' then
         Values (1) := Values (1) / Values (2);
         Pop_Value;
      elsif Last = 4 and then Operation (1 .. 4) = "exit" then
         exit Main_Loop;
      else
         Push_Value;
         F_IO.Get (From => Operation, Item => Values (1), Last => Last);
      end if;
      
      Display_Loop:
      for I in reverse Value_Array'Range loop
         F_IO.Put
           (Item => Values (I),
            Fore => F_IO.Default_Fore,
            Aft  => F_IO.Default_Aft,
            Exp  => 4);
         T_IO.New_Line;
      end loop Display_Loop;
   end loop Main_Loop;

   return;
end Numeric_3;

所有指数函数都在泛型包 Ada.Numerics.Generic_Elementary_Functions 中定义。

形式为 的计算由运算符 ** 执行。注意:此运算符有两个版本。预定义的运算符 ** 只允许使用 Standard.Integer 作为指数。如果你需要使用浮点类型作为指数,你需要使用 **,它定义在 Ada.Numerics.Generic_Elementary_Functions 中。

平方根 由函数 Sqrt() 计算。没有定义计算任意根 的函数。但是可以使用对数来计算任意根,使用数学恒等式: 在 Ada 中将变为 root := Exp (Log (a) / b)。或者,使用 在 Ada 中为 root := a**(1.0/b)

Ada.Numerics.Generic_Elementary_Functions 定义了任意对数 和自然对数 的函数,它们都具有相同的名称 Log(),区别在于参数的数量。

演示

[edit | edit source]

以下扩展演示展示了如何在 Ada 中使用指数函数。新的演示还使用 Unbounded_String 而不是字符串,这使得比较更容易。

请注意,从现在开始我们不会再复制完整的源代码了。请按照下载链接查看完整的程序。

文件:numeric_4.adb (view, plain text, download page, browse all)
with Ada.Text_IO;
with Ada.Numerics.Generic_Elementary_Functions;
with Ada.Strings.Unbounded;

procedure Numeric_4 is
  package Str renames Ada.Strings.Unbounded;
  package T_IO renames Ada.Text_IO;

  procedure Pop_Value;
  procedure Push_Value;
  function Get_Line return Str.Unbounded_String;

  type Value_Type is digits 12 range
     -999_999_999_999.0e999 .. 999_999_999_999.0e999;

  type Value_Array is array (Natural range 1 .. 4) of Value_Type;

  package F_IO is new Ada.Text_IO.Float_IO (Value_Type);
  package Value_Functions is new Ada.Numerics.Generic_Elementary_Functions (
     Value_Type);

  use Value_Functions;
  use type Str.Unbounded_String;

  Values    : Value_Array := (others => 0.0);
  Operation : Str.Unbounded_String;
  Dummy     : Natural;

  function Get_Line return Str.Unbounded_String is
     BufferSize : constant := 2000;
     Retval     : Str.Unbounded_String := Str.Null_Unbounded_String;
     Item       : String (1 .. BufferSize);
     Last       : Natural;
  begin
     Get_Whole_Line :
        loop
           T_IO.Get_Line (Item => Item, Last => Last);

           Str.Append (Source => Retval, New_Item => Item (1 .. Last));

           exit Get_Whole_Line when Last < Item'Last;
        end loop Get_Whole_Line;

     return Retval;
  end Get_Line;

...

begin
  Main_Loop :
     loop
        T_IO.Put (">");
        Operation := Get_Line;

...
        elsif Operation = "e" then -- insert e
           Push_Value;
           Values (1) := Ada.Numerics.e;
        elsif Operation = "**" or else Operation = "^" then -- power of x^y
           Values (1) := Values (1) ** Values (2);
           Pop_Value;
        elsif Operation = "sqr" then -- square root
           Values (1) := Sqrt (Values (1));
        elsif Operation = "root" then -- arbritary root
           Values (1) :=
              Exp (Log (Values (2)) / Values (1));
           Pop_Value;
        elsif Operation = "ln" then -- natural logarithm
           Values (1) := Log (Values (1));
        elsif Operation = "log" then -- based logarithm
           Values (1) :=
              Log (Base => Values (1), X => Values (2));
           Pop_Value;
        elsif Operation = "exit" then
           exit Main_Loop;
        else
           Push_Value;
           F_IO.Get
             (From => Str.To_String (Operation),
              Item => Values (1),
              Last => Dummy);
        end if;

...
     end loop Main_Loop;

  return;
end Numeric_4;

高等数学

[edit | edit source]

一整套 三角函数 定义在泛型包 Ada.Numerics.Generic_Elementary_Functions 中。所有函数都针对 2π 和任意循环值(一次完整的旋转周期)定义。

请注意调用 Arctan () 函数的不同之处。

文件:numeric_5.adb (view, plain text, download page, browse all)
with Ada.Text_IO;
with Ada.Numerics.Generic_Elementary_Functions;
with Ada.Strings.Unbounded;

procedure Numeric_5 is

...

  procedure Put_Line (Value : in Value_Type);

  use Value_Functions;
  use type Str.Unbounded_String;

  Values    : Value_Array := (others => 0.0);
  Cycle     : Value_Type  := Ada.Numerics.Pi;
  Operation : Str.Unbounded_String;
  Dummy     : Natural;

...

  procedure Put_Line (Value : in Value_Type) is
  begin
     if abs Value_Type'Exponent (Value) >=
        abs Value_Type'Exponent (10.0 ** F_IO.Default_Aft)
     then
        F_IO.Put
          (Item => Value,
           Fore => F_IO.Default_Aft,
           Aft  => F_IO.Default_Aft,
           Exp  => 4);
     else
        F_IO.Put
          (Item => Value,
           Fore => F_IO.Default_Aft,
           Aft  => F_IO.Default_Aft,
           Exp  => 0);
     end if;
     T_IO.New_Line;

     return;
  end Put_Line;

...

begin
  Main_Loop :
     loop
        Display_Loop :
           for I in reverse  Value_Array'Range loop
              Put_Line (Values (I));
           end loop Display_Loop;
        T_IO.Put (">");
        Operation := Get_Line;

...
        elsif Operation = "deg" then -- switch to degrees
           Cycle := 360.0;
        elsif Operation = "rad" then -- switch to degrees
           Cycle := Ada.Numerics.Pi;
        elsif Operation = "grad" then -- switch to degrees
           Cycle := 400.0;
        elsif Operation = "pi" or else Operation = "π" then -- switch to degrees
           Push_Value;
           Values (1) := Ada.Numerics.Pi;
        elsif Operation = "sin" then -- sinus
           Values (1) := Sin (X => Values (1), Cycle => Cycle);
        elsif Operation = "cos" then -- cosinus
           Values (1) := Cos (X => Values (1), Cycle => Cycle);
        elsif Operation = "tan" then -- tangents
           Values (1) := Tan (X => Values (1), Cycle => Cycle);
        elsif Operation = "cot" then -- cotanents
           Values (1) := Cot (X => Values (1), Cycle => Cycle);
        elsif Operation = "asin" then -- arc-sinus
           Values (1) := Arcsin (X => Values (1), Cycle => Cycle);
        elsif Operation = "acos" then -- arc-cosinus
           Values (1) := Arccos (X => Values (1), Cycle => Cycle);
        elsif Operation = "atan" then -- arc-tangents
           Values (1) := Arctan (Y => Values (1), Cycle => Cycle);
        elsif Operation = "acot" then -- arc-cotanents
           Values (1) := Arccot (X => Values (1), Cycle => Cycle);

...
     end loop Main_Loop;

  return;
end Numeric_5;


该演示还包含改进的数字输出,其行为更像普通计算器。

你猜对了:一整套双曲函数定义在泛型包 Ada.Numerics.Generic_Elementary_Functions 中。

文件:numeric_6.adb (view, plain text, download page, browse all)
with Ada.Text_IO;
with Ada.Numerics.Generic_Elementary_Functions;
with Ada.Strings.Unbounded;
with Ada.Exceptions;

procedure Numeric_6 is
  package Str renames Ada.Strings.Unbounded;
  package T_IO renames Ada.Text_IO;
  package Exept renames Ada.Exceptions;

...

 begin
  Main_Loop :
     loop
        Try :
           begin
              Display_Loop :
...
              elsif Operation = "sinh" then -- sinus hyperbolic
                 Values (1) := Sinh (Values (1));
              elsif Operation = "cosh" then -- cosinus hyperbolic
                 Values (1) := Coth (Values (1));
              elsif Operation = "tanh" then -- tangents hyperbolic
                 Values (1) := Tanh (Values (1));
              elsif Operation = "coth" then -- cotanents hyperbolic
                 Values (1) := Coth (Values (1));
              elsif Operation = "asinh" then -- arc-sinus hyperbolic
                 Values (1) := Arcsinh (Values (1));
              elsif Operation = "acosh" then -- arc-cosinus hyperbolic
                 Values (1) := Arccosh (Values (1));
              elsif Operation = "atanh" then -- arc-tangents hyperbolic
                 Values (1) := Arctanh (Values (1));
              elsif Operation = "acoth" then -- arc-cotanents hyperbolic
                 Values (1) := Arccoth (Values (1));
...
           exception
              when An_Exception : others =>
                 T_IO.Put_Line
                   (Exept.Exception_Information (An_Exception));
           end Try;
     end loop Main_Loop;

  return;
end Numeric_6;

作为额外奖励,此版本支持错误处理,因此在执行非法计算时不会直接崩溃。

对于 复数运算,Ada 提供了包 Ada.Numerics.Generic_Complex_Types。该包是“特殊需求附录”的一部分,这意味着它是可选的。开源 Ada 编译器 GNAT 实现了所有“特殊需求附录”,因此可以使用复数运算。

由于 Ada 支持用户自定义运算符,所有 (+, -, *) 运算符在包 Ada.Numerics.Generic_Complex_Types 被实例化 (package ... is new ...) 并且类型被设置为可见 (use type ...) 后,就拥有了它们通常的含义。

Ada 还提供了包 Ada.Text_IO.Complex_IOAda.Numerics.Generic_Complex_Elementary_Functions,它们提供了与普通对应项类似的功能。但是,它们之间存在一些差异。

因此,只需进行一些修改,就可以将 "正常" 计算器转换为复数运算计算器。

文件:numeric_7.adb (查看, 纯文本, 下载页面, 浏览所有)
with Ada.Text_IO.Complex_IO;
with Ada.Numerics.Generic_Complex_Types;
with Ada.Numerics.Generic_Complex_Elementary_Functions;
with Ada.Strings.Unbounded;
with Ada.Exceptions; 

procedure Numeric_7 is

...
 
  package Complex_Types is new Ada.Numerics.Generic_Complex_Types (
     Value_Type);
  package Complex_Functions is new
     Ada.Numerics.Generic_Complex_Elementary_Functions (
     Complex_Types);
  package C_IO is new Ada.Text_IO.Complex_IO (Complex_Types);

  type Value_Array is
     array (Natural range 1 .. 4) of Complex_Types.Complex;

  procedure Put_Line (Value : in Complex_Types.Complex);

  use type Complex_Types.Complex;
  use type Str.Unbounded_String;
  use Complex_Functions;

  Values    : Value_Array :=
     (others => Complex_Types.Complex'(Re => 0.0, Im => 0.0));

...
 
  procedure Put_Line (Value : in Complex_Types.Complex) is
  begin
     if (abs Value_Type'Exponent (Value.Re) >=
         abs Value_Type'Exponent (10.0 ** C_IO.Default_Aft))
       or else (abs Value_Type'Exponent (Value.Im) >=
                abs Value_Type'Exponent (10.0 ** C_IO.Default_Aft))
     then
        C_IO.Put
          (Item => Value,
           Fore => C_IO.Default_Aft,
           Aft  => C_IO.Default_Aft,
           Exp  => 4);
     else
        C_IO.Put
          (Item => Value,
           Fore => C_IO.Default_Aft,
           Aft  => C_IO.Default_Aft,
           Exp  => 0);
     end if;
     T_IO.New_Line;

     return;
  end Put_Line;

begin

...
              elsif Operation = "e" then -- insert e
                 Push_Value;
                 Values (1) :=
                    Complex_Types.Complex'(Re => Ada.Numerics.e, Im => 0.0);

...

              elsif Operation = "pi" or else Operation = "π" then -- insert pi
                 Push_Value;
                 Values (1) :=
                    Complex_Types.Complex'(Re => Ada.Numerics.Pi, Im => 0.0);
              elsif Operation = "sin" then -- sinus
                 Values (1) := Sin (Values (1));
              elsif Operation = "cos" then -- cosinus
                 Values (1) := Cot (Values (1));
              elsif Operation = "tan" then -- tangents
                 Values (1) := Tan (Values (1));
              elsif Operation = "cot" then -- cotanents
                 Values (1) := Cot (Values (1));
              elsif Operation = "asin" then -- arc-sinus
                 Values (1) := Arcsin (Values (1));
              elsif Operation = "acos" then -- arc-cosinus
                 Values (1) := Arccos (Values (1));
              elsif Operation = "atan" then -- arc-tangents
                 Values (1) := Arctan (Values (1));
              elsif Operation = "acot" then -- arc-cotanents
                 Values (1) := Arccot (Values (1));

...

  return;
end Numeric_7;

Ada 支持 向量矩阵 运算,适用于正常的实数类型和复数类型。对于这些类型,使用泛型包 Ada.Numerics.Generic_Real_Arrays 和 Ada.Numerics.Generic_Complex_Arrays。这两个包都提供了通常的运算集,但没有 I/O 包,并且没有用于基本函数的包。

由于没有用于向量和矩阵 I/O 的包,因此创建演示要复杂得多,因此尚未准备好。可以查看当前的进度,它将是一个合并所有功能的通用计算器。

状态:停滞 - 对于向量和矩阵堆栈,我们需要 Indefinite_Vectors - 它们目前不是 GNAT/Pro 的一部分。好吧,我可以使用 booch 组件......

文件:numeric_8-complex_calculator.ada (查看, 纯文本, 下载页面, 浏览所有)
文件:numeric_8-get_line.ada (查看, 纯文本, 下载页面, 浏览所有)
文件:numeric_8-real_calculator.ada (查看, 纯文本, 下载页面, 浏览所有)
文件:numeric_8-real_vector_calculator.ada (查看, 纯文本, 下载页面, 浏览所有)

维基教科书

[编辑 | 编辑源代码]

Ada 95 参考手册

[编辑 | 编辑源代码]

Ada 2005 参考手册

[编辑 | 编辑源代码]
华夏公益教科书