跳轉到內容

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 參考手冊

[編輯 | 編輯原始碼]
華夏公益教科書