跳至內容

Ada 程式設計/輸入輸出/文字示例

來自 Wikibooks,為開放世界提供開放書籍

Ada. Time-tested, safe and secure.
Ada。經時間考驗,安全可靠。


我們中那些有美國朋友的人經常不得不處理華氏溫度標度。雖然世界其他地方已經採用攝氏溫度標度,但美國和其他一些國家仍然忠於華氏度。

雖然在與朋友打交道時,這是一個既小又無關緊要的問題,但它仍然是一個值得擁有自己的程式的問題:華氏度到攝氏度的轉換器。

必要的資料夾和目錄

[編輯 | 編輯原始碼]

在我們開始之前,我們需要做一些準備。首先在您的計算機上建立以下目錄/檔案結構

$ mkdir FtoC
$ cd FtoC
$ mkdir exe objects
$ touch ftoc.adb ftoc.gpr

上面應該在 FtoC 目錄中留下以下內容

 exe/
 objects/
 ftoc.adb
 ftoc.gpr

我們將以以下方式使用這兩個目錄和檔案

  • exe/ 這是編譯程式時 ftoc 可執行檔案放置的目錄
  • objects/ 編譯程式時,編譯器會建立 ALI 檔案、目標檔案和樹檔案。這些檔案將放置在此目錄中。
  • ftoc.adb ftoc 程式的主要 Ada 原始檔。
  • ftoc.gpr 這是一個 Ada 專案檔案。此檔案控制程式的各種屬性,例如如何編譯它,包含哪些原始碼,將檔案放在哪裡等等。
專案檔案
[編輯 | 編輯原始碼]

將此新增到 ftoc.gpr 檔案中

project ftoc is
   for Source_Dirs use (".");
   for Main use ("ftoc.adb");
   for Exec_Dir use "exe";
   for Object_Dir use "objects";

   package Ide is
      for Compiler_Command ("ada") use "/usr/gnat/bin/gnatmake";
   end Ide;

   package Compiler is
      Common_Options := ("-gnatwa",
                         "-gnaty3abcdefhiklmnoprstux",
                         "-Wall",
                         "-O2");
     for Default_Switches ("Ada") use Common_Options;
   end Compiler;
end ftoc;

請前往 此處 瞭解有關上面專案檔案的不同部分的說明。

實際程式
[編輯 | 編輯原始碼]

專案檔案已完成,我們現在將注意力轉向實際的 ftoc 程式碼。將此新增到 ftoc.adb 檔案中

with Ada.Text_IO;          use Ada.Text_IO;
with Ada.Integer_Text_IO;  use Ada.Integer_Text_IO;
with Ada.Float_Text_IO;    use Ada.Float_Text_IO;

procedure FtoC is
   subtype Fahrenheit_Degree_Range is Natural range 0 .. 212;
   -- Now, that is a Natural Range for Ada, but I can tell you that Americans
   -- are not nearly so happy with "0" and not even an Icelander likes it at "212"!
   Fahr     : Fahrenheit_Degree_Range := Fahrenheit_Degree_Range'First;
   Factor   : constant  := 5.0 / 9.0;
   Offset   : constant  := 32;
   Step     : constant  := 1;
begin
   loop
      Put (Item  => Fahr, Width => Fahrenheit_Degree_Range'Width);
      Put (Item => Factor* Float (Fahr - Offset),
           Fore => 4,
           Aft  => 2,
           Exp  => 0);
      New_Line;
      exit when Fahr = Fahrenheit_Degree_Range'Last;
      Fahr := Fahr + Step;
   end loop;
end FtoC;

儲存檔案,然後我們繼續最後一步:編譯。

編譯並執行 FtoC

[編輯 | 編輯原始碼]

如果您使用優秀的 GNAT Studio IDE,編譯只需簡單地按下 F4 來編譯專案,並按下 Shift+F2 來執行它。如果您沒有使用此 IDE,則執行以下操作

$ gnatmake -P ftoc.gpr

您應該會看到一些輸出滾動

 gcc -c -gnatwa -gnaty3abcdefhiklmnoprstux -Wall -O2 -I- -gnatA /home/thomas/FtoC/ftoc.adb
 gnatbind -I- -x /home/thomas/FtoC/objects/ftoc.ali
 gnatlink /home/thomas/FtoC/objects/ftoc.ali -o /home/thomas/FtoC/exe/ftoc

您現在在 exe/ 目錄中有一個可執行檔案。當您執行它時,您會得到以下內容(此處略微縮寫)

 0 -17.78
 1 -17.22
 2 -16.67
 ...
 26  -3.33
 27  -2.78
 28  -2.22
 29  -1.67
 30  -1.11
 31  -0.56
 32   0.00
 33   0.56
 34   1.11
 35   1.67
 ...
 48   8.89
 49   9.44
 50  10.00
 51  10.56
 52  11.11
 ...
 67  19.44
 68  20.00
 69  20.56
 70  21.11
 71  21.67
 ...
 84  28.89
 85  29.44
 86  30.00
 87  30.56
 ...
 210  98.89
 211  99.44
 212 100.00

現在我們知道 0 華氏度等於 -17.78 攝氏度,在 212 華氏度時水沸騰,對於我們攝氏度使用者來說,這是 100 攝氏度。該程式可以正常工作!讓我們詳細地回顧一下,以瞭解它是如何工作的。

注意: 以下示例中的所有輸出都將大幅縮寫,因為為每個小示例列印 213 行輸出簡直是瘋狂。

FtoC 程式如何工作?
[編輯 | 編輯原始碼]

讓我們從前三行開始

with Ada.Text_IO;          use Ada.Text_IO;
with Ada.Integer_Text_IO;  use Ada.Integer_Text_IO;
with Ada.Float_Text_IO;    use Ada.Float_Text_IO;

這些被稱為 with 子句和 use 宣告。Ada with 子句可以比作 C #include。當您看到 with Ada.Text_IO 時,這意味著 Ada.Text_IO 包的實用程式將提供給程式。當您接下來看到宣告 use Ada.Text_IO 時,這意味著 Ada.Text_IO 的實用程式可以直接被程式看到,即您不必編寫

Ada.Text_IO.Put ("No use clause");

來呼叫 Put 過程。相反,您只需執行

Put ("With use clause");

FtoC 程式中,我們利用並使其可見三個包:Ada.Text_IOAda.Integer_Text_IOAda.Flot_Text_IO。這些包提供了它們名稱所暗示的確切含義:不同型別的文字 IO 功能。

檢視原始碼,您可能會想知道 Ada.Text_IO 的必要性在哪裡,因為我們只輸出數字。我們會很快講到這一點,敬請關注。

FtoC 宣告

[編輯 | 編輯原始碼]

接下來我們有這段程式碼

procedure FtoC is
   subtype Fahrenheit_Degree_Range is Natural range 0 .. 212;
   Fahr     : Fahrenheit_Degree_Range := Fahrenheit_Degree_Range'First;
   Factor   : constant  := 5.0 / 9.0;
   Offset   : constant  := 32;
   Step     : constant  := 1;
begin

這被稱為 Ada 程式的宣告部分。在這裡,我們宣告變數、常量、型別等等。在 FtoC 的情況下,我們有 5 個宣告。讓我們談談前兩個

subtype Fahrenheit_Degree_Range is Natural range 0 .. 212;
Fahr     : Fahrenheit_Degree_Range := Fahrenheit_Degree_Range'First;

這正是 Ada 最大的賣點之一:能夠建立自己的型別,並設定自己的約束。它可能看起來並不重要,但相信我,它確實很重要。在這種情況下,我們建立了內建子型別 Natural (3.5.4 [帶註釋的]) 的一個新 子型別。我們將該型別的範圍約束為 0 .. 212,這意味著宣告為 Fahrenheit_Degree_Range 型別的物件永遠 不能 低於 0 或高於 212。如果將超出此範圍的值分配給 Fahrenheit_Degree_Range 型別 的物件,則會引發 Constraint_Error 異常。

有了 Fahrenheit_Degree_Range 型別,我們現在將注意力轉向 Fahr 變數的宣告和賦值。如果您從未見過此語法或它對您來說毫無意義,請閱讀本維基上的 常量 文章,然後返回此處。

Fahr 變數是 Fahrenheit_Degree_Range 型別的,其初始值為 0。為什麼是 0?因為我們將其賦值為 Fahrenheit_Degree_Range'First,而 'First 部分等於該類型範圍約束中的第一個值,在本例中為 0。因此,Fahrenheit_Degree_Range'Last 的值為 212。

讓我們看一下最後三個物件宣告

Factor   : constant  := 5.0 / 9.0;
Offset   : constant  := 32;
Step     : constant  := 1;

這裡發生了什麼?為什麼這些宣告中沒有與任何宣告相關的 type?好吧,如果您閱讀了 常量 文章,您將知道這些常量被稱為 命名數字,它們的 type 被稱為通用型別 (3.4.1 [帶註釋的])。命名數字可以是任何大小和精度。

FactorOffset 常量用於華氏度到攝氏度的計算,而 Step 定義了我們在程式中執行的轉換次數。

FtoC 實體

[編輯 | 編輯原始碼]

宣告已完成,我們現在將注意力轉向程式的實體

begin
   loop
      Put (Item  => Fahr, Width => Fahrenheit_Degree_Range'Width);
      Put (Item => Factor* Float (Fahr - Offset),
           Fore => 4,
           Aft  => 2,
           Exp  => 0);
      New_Line;
      exit when Fahr = Fahrenheit_Degree_Range'Last;
      Fahr := Fahr + Step;
   end loop;
end FtoC;

保留字 begin 表示 body 的開始,而相同的 body 以最終的 end FtoC 結束。在這兩者之間,我們有一堆語句。

第一個是 loop 語句。 迴圈 在 Ada 中以多種不同的形式出現,每種形式都遵循迴圈的基本前提

loop
   --  some statements
end loop;

迴圈可以使用 exit 關鍵字終止

loop
   --  some statements
   if X = Y then
      exit;
   end if;
end loop;

這種結構非常常見,因此提供了一個簡短版本

loop
   --  some statements
   exit when X = Y;
end loop;

正是我們最後使用的版本在 FtoC 程式中,當 Fahr 等於 Fahrenheit_Degree_Range 型別的 'Last 值時,我們將退出迴圈。

FtoC 輸出 - 將整數放在螢幕上

[編輯 | 編輯原始碼]

緊隨 loop 語句之後,我們遇到了 Put 過程

Put (Item  => Fahr, Width => Fahrenheit_Degree_Range'Width);

我們從宣告中知道 FahrNatural 的子型別,而 Natural 又 是 Integer 的子型別,因此這一行對 Put 的呼叫實際上呼叫了 Ada.Integer_Text_IO.Put。如您所見,我們為 Put 提供了兩個引數:ItemWidthItem 的含義應該是顯而易見的:它是我們想要輸出的整數,在本例中是 Fahr 變數。

另一方面,Width 並不那麼直觀。Width 引數指定了將整數型別(在本例中是子型別)作為字面量輸出所需的最小字元數,再加上可能出現的負號所需的 1 個字元。如果 Width 引數設定過高,整數字面量將用空格填充。如果設定過低,它將根據需要自動擴充套件。將 Width 引數設定為 0,會導致欄位的寬度為包含整數所需的最小寬度。

'Width 屬性返回型別的最大寬度,因此如果我們稍後更改 Fahrenheit_Degree_Range,我們就不必對這個對 Put 的呼叫做任何事情;它會自動相應調整。

讓我們使用不同的 Width 引數進行一些測試

Put (Item  => Fahr); --  Default Width parameter

輸出現在看起來像這樣

          0 -17.78
          1 -17.22
          2 -16.67
          3 -16.11
          4 -15.56
          ...

有很多浪費的空間。這是因為 Put 現在為 Integer 預留了空間,而 Fahrenheit_Degree_Range 是從該型別派生的。讓我們嘗試使用 0

Put (Item  => Fahr, Width => 0); --  Minimum required characters for the integer

以及輸出

 0 -17.78
 1 -17.22
 2 -16.67
 3 -16.11
 9 -12.78
 10 -12.22
 11 -11.67
 99  37.22
 100  37.78
 101  38.33
 ...

不幸的是,為了提高可讀性,Fahr 整數字面量不再右對齊。每個數字都將獲得包含它所需的精確寬度,不多也不少。

讓我們嘗試一個足夠容納部分 Fahr 值,但不能容納所有值的 Width

Put (Item  => Fahr, Width => 2); --  Minimum width of 2. Expands if necessary

這將輸出

  0 -17.78
  1 -17.22
  2 -16.67
  9 -12.78
 10 -12.22
 11 -11.67
 98  36.67
 99  37.22
 100  37.78
 101  38.33
 102  38.89
 103  39.44
 ...

如您所見,一位數的值右對齊並用 1 個空格填充,兩位數的值輸出一致,但其餘結果則擴充套件以容納第三個字元。

FtoC 輸出 - 現在使用浮點數

[編輯 | 編輯原始碼]

在處理整數型別的 Put 之後,我們繼續進行下一個 Put>

Put (Item => Factor* Float (Fahr - Offset),
     Fore => 4,
     Aft  => 2,
     Exp  => 0);

Put 的這個呼叫中的 Item 引數是一個浮點數,因為 Factor 是一個包含小數點的命名數字,表示式 Fahr - Offset 使用 Float (Fahr - Offset) 表示式轉換為浮點數。因此,在呼叫這個 Put 時,我們實際上呼叫的是 Ada.Float_Text_IO.Put

Fore 引數指定了在小數點之前輸出值所需的最小字元數。與整數型別的 Width 一樣,Fore 會在必要時自動擴充套件。Aft 設定小數點後的精度,在本例中為 2。最後,Exp 設定指數字段大小。Exp => 0 的值表示不輸出指數。非零值將輸出指數符號“E”,+/–,以及指數的數字。注意:Exp 的值不應小於零!

讓我們嘗試幾種不同的組合

Put (Item => Factor* Float (Fahr - Offset),
     Fore => 10,
     Aft  => 4,
     Exp  => 0);

輸出

   0       -17.7778
   1       -17.2222
  15        -9.4444
  16        -8.8889
  17        -8.3333
  26        -3.3333
  27        -2.7778
 100        37.7778
 101        38.3333
 102        38.8889
 103        39.4444
 104        40.0000
 ...

或者,試試這個

Put (Item => Factor* Float (Fahr - Offset),
     Fore => 4,
     Aft  => 2,
     Exp  => 1);

以及輸出

  0  -1.78E+1
  1  -1.72E+1
  2  -1.67E+1
  8  -1.33E+1
  9  -1.28E+1
 10  -1.22E+1
 11  -1.17E+1
 18  -7.78E+0
 37   2.78E+0
 98   3.67E+1
 99   3.72E+1
100   3.78E+1
101   3.83E+1
...

最後

Put (Item => Factor* Float (Fahr - Offset),
     Fore => 0,
     Aft  => 1,
     Exp  => 0);

這將輸出

   0-17.8
   8-13.3
   9-12.8
  10-12.2
  11-11.7
  31-0.6
  320.0
  499.4
  9836.7
  9937.2
 10037.8
 10138.3
 10238.9
 ...

這顯然看起來不太美觀。

換行符、退出策略和步長

[編輯 | 編輯原始碼]

FtoC 程式的最後四行完成了我們的格式化,如果我們已經完成則退出,否則將 Fahr 的下一個值設定為要轉換的值

   New_Line;
   exit when Fahr = Fahrenheit_Degree_Range'Last;
   Fahr := Fahr + Step;
end FtoC;

New_Line 的唯一呼叫是我們在使用 with Ada.Text_IO 使 Ada.Text_IO 可用於 FtoC 程式的原因。New_Line 的作用是輸出一個換行符。如果沒有對 New_Line 的呼叫,程式執行後將產生如下所示的輸出

  0 -17.78   1 -17.22   2 -16.67   3 -16.11   4 -15.56   5 -15.00   6 -14.44   7 -13.89   8 -13.33   9 -12.78  10 -12.22  11 -11.67 ...

New_Line 過程接受一個 Spacing 引數,這意味著您可以執行以下操作來輸出連續的換行符

New_Line (Spacing => 5);

或者,簡單地

New_Line (5);

我們已經討論了 終止迴圈exit when 方法,最後一條語句只是一個簡單的計數器。Fahr 的值在每次迴圈迭代時都會以 Step 增加。當 Fahr 等於 Fahrenheit_Degree_Range'Last 時,迴圈將終止。

最後一行 end Ftoc; 表示程式的結束。沒有更多要做的,也沒有更多要看的。控制權將返回給最初呼叫程式的任何內容,生命繼續。

希望您喜歡這個關於構建華氏度到攝氏度轉換表的簡短教程。留給讀者去弄清楚如何新增開氏度。祝您玩得開心!

另請參見

[編輯 | 編輯原始碼]

華夏公益教科書

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