跳轉到內容

Ada 程式設計/容器

來自 Wikibooks,開放世界中的開放書籍

Ada. Time-tested, safe and secure.
Ada。經久耐用、安全可靠。

以下是某些容器型別的簡單演示。它沒有涵蓋所有內容,但應該可以幫助您入門。

此語言功能僅從Ada 2005開始可用。

第一個示例:對映

[編輯 | 編輯原始碼]

下面的程式用多種人類語言向世界問好。問候語儲存在一個表格或雜湊對映中。該對映將每個問候語(一個值)與語言程式碼(一個鍵)相關聯。也就是說,您可以使用語言程式碼作為鍵在表格中查詢問候語值。

對映中的元素是國際字元的常量字串,或者更確切地說,是指向此類常量字串的指標。一個包區域用於設定語言 ID 和Ada.Containers.Hashed_Maps的一個例項。

檔案:regional.ads (檢視純文字下載頁面瀏覽所有)
with Ada.Containers.Hashed_Maps;  use Ada.Containers;

package Regional is

   type Language_ID is (DE, EL, EN, ES, FR, NL);
   --  a selection from the two-letter codes for human languages

   type Hello_Text is access constant Wide_String;
   --  objects will contain a «hello»-string in some language


   function ID_Hashed (id: Language_ID) return Hash_Type;
   --  you need to provide this to every hashed container

   package Phrases is new Ada.Containers.Hashed_Maps
     (Key_Type => Language_ID,
      Element_Type => Hello_Text,
      Hash => ID_Hashed,
      Equivalent_Keys => "=");

end Regional;

這是程式,稍後將解釋詳細資訊。

檔案:hello_world_extended.ads (檢視純文字下載頁面瀏覽所有)
with Regional; use Regional;
with Ada.Wide_Text_IO; use Ada;

procedure Hello_World_Extended is

   --  print greetings in different spoken languages

   greetings: Phrases.Map;
   --  the dictionary of greetings

begin -- Hello_World_Extended

   Phrases.Insert(greetings,
                  Key => EN,
                  New_Item => new Wide_String'("Hello, World!"));

   --  or, shorter,
   greetings.Insert(DE, new Wide_String'("Hallo, Welt!"));
   greetings.Insert(NL, new Wide_String'("Hallo, Wereld!"));
   greetings.Insert(ES, new Wide_String'("¡Hola mundo!")); 
   greetings.Insert(FR, new Wide_String'("Bonjour, Monde!"));
   greetings.Insert(EL, new Wide_String'("Γεια σου κόσμε"));

   declare
      use Phrases;

      speaker: Cursor := First(greetings);
   begin
      while Has_Element(speaker) loop
         Wide_Text_IO.Put_Line( Element(speaker).all );
         Next(speaker);
      end loop;
   end;

end Hello_World_Extended;

第一個插入語句以 Ada 95 樣式編寫

  Phrases.Insert(greetings,
                 Key => EN,
                 New_Item => new Wide_String'("Hello, World!"));

接下來的插入使用所謂的區分接收者表示法,您可以在 Ada 2005 中使用。(這是面向物件的術語。雖然 Insert 呼叫涉及以下所有內容:容器物件(greetings)、鍵物件(EN)和新專案物件(new Wide_String'("Hello, World!")),但容器物件與其他物件的區別在於,Insert 呼叫為其(且僅為其)提供其他物件。在本例中,容器物件將透過呼叫進行修改,使用名為 Key 和 New_Item 的引數進行修改。)

  greetings.Insert(ES, new Wide_String'("¡Hola mundo!"));

設定表格後,程式繼續打印表格中包含的所有問候語。它透過一個沿表格中的元素以某種順序執行的游標來實現此目的。典型方案是獲取一個游標,此處使用First,然後迭代以下呼叫

  1. Has_Element,用於檢查游標是否位於元素上
  2. Element,以獲取元素和
  3. Next,以將游標移動到另一個元素

當沒有更多元素剩餘時,游標將具有特殊值No_Element。實際上,這是一種可以與Ada.Containers子包中的所有容器一起使用的迭代方案。

稍作修改:選擇元素

[編輯 | 編輯原始碼]

下一個程式演示瞭如何根據鍵從對映中選擇一個值。實際上,您將提供鍵。該程式與前面的程式類似,只是它不僅列印對映中的所有元素,而且根據從標準輸入讀取的 Language_ID 值選擇一個元素。

檔案:hello_world_pick.adb (檢視純文字下載頁面瀏覽所有)
with Regional; use Regional;
with Ada.Wide_Text_IO; use Ada;

procedure Hello_World_Pick is

  ... as before ...

 declare
     use Phrases;
 
     package Lang_IO is new Wide_Text_IO.Enumeration_IO(Language_ID);
     lang: Language_ID;
  begin
     Lang_IO.Get(lang);
     Wide_Text_IO.Put_Line( greetings.Element(lang).all );
  end;
 
end Hello_World_Pick;

這次的Element函式使用一個鍵 (lang) 而不是一個游標。實際上,它使用兩個值,另一個值是greetings,以區分接收者表示法。

第二個示例:向量和對映

[編輯 | 編輯原始碼]

讓我們從字面上理解豆子計數。紅豆、綠豆和白豆。(是的,白豆確實存在。)您的工作是收集一定數量的豆子,稱重,然後分別確定紅豆、綠豆和白豆的平均重量。這是一種方法。

同樣,我們需要一個包,這次用於儲存與蔬菜相關的資訊。介紹一下Beans包(Grams 型別不屬於蔬菜包,但它在那裡是為了簡化操作)

檔案:1/beans.ads (檢視純文字下載頁面瀏覽所有)
with Ada.Containers.Vectors;

package Beans is

   type Bean_Color is (R, G, W);
   --  red, green, and white beans

   type Grams is delta 0.01 digits 7;
   --  enough to weigh things as light as beans but also as heavy as
   --  many of them

   type Bean is
   --  info about a single bean
      record
         kind: Bean_Color;
         weight: Grams;
      end record;

   subtype Bean_Count is Positive range 1 .. 1_000;
   --  numbers of beans to count (how many does Cinderella have to count?)

   package Bean_Vecs is new Ada.Containers.Vectors
     (Element_Type => Bean,
      Index_Type => Bean_Count);

end Beans;

Vectors例項提供了一個類似於陣列的資料結構,該資料結構可以在執行時更改其大小。它被稱為Vector。讀取的每個豆子都將附加到一個Bean_Vecs.Vector物件。

以下程式首先呼叫read_input以用豆子填充緩衝區。接下來,它呼叫一個函式,該函式計算具有相同顏色的豆子的平均重量。此函式

with Beans;   use Beans;

function average_weight
  (buffer: Bean_Vecs.Vector; desired_color: Bean_Color) return Grams;
--  scan `buffer` for all beans that have `desired_color`. Compute the
--  mean of their `.weight` components


然後列印每種顏色的豆子的平均值,程式停止。

檔案:1/bean_counting.adb (檢視純文字下載頁面瀏覽所有)
with Beans;
with average_weight;
with Ada.Wide_Text_IO;

procedure bean_counting is
   use Beans, Ada;

   buffer: Bean_Vecs.Vector;

   procedure read_input(buf: in out Bean_Vecs.Vector) is separate;
   --  collect information from a series of bean measurements into `buf`


begin --  bean_counting

   read_input(buffer);

   --  now everything is set up for computing some statistical data.
   --  For every bean color in `Bean_Color`, the function `average_weight`
   --  will scan `buffer` once, and accumulate statistical data from
   --  each element encountered.

   for kind in Bean_Color loop
      Wide_Text_IO.Put_Line
        (Bean_Color'Wide_Image(kind) &
         " ø =" & Grams'Wide_Image( average_weight(buffer, kind) ));
   end loop;

end bean_counting;

所有容器操作都在函式average_weight中進行。為了找到相同顏色的豆子的平均重量,該函式正在按順序檢視所有豆子。如果一個豆子具有正確的顏色,average_weight將它的重量新增到總重量中,並將計數的豆子數量增加 1。

計算訪問所有豆子。從一個豆子移動到下一個豆子,然後執行上述步驟所需的迭代最好留給Iterate過程,它是所有容器包的一部分。為此,將上述步驟包裝在某個過程中,並將此過程傳遞給Iterate。效果是Iterate為向量中的每個元素呼叫您的過程,並將游標值傳遞給您的過程,每個元素一個。

讓容器機制執行迭代也可能比自己移動和檢查游標更快,就像在Hello_World_Extended示例中所做的那樣。

檔案:average_weight.adb (檢視純文字下載頁面瀏覽所有)
with Beans;  use Beans.Bean_Vecs;

function average_weight
  (buffer: Bean_Vecs.Vector; desired_color: Bean_Color) return Grams
is
   total: Grams := 0.0;
   --  weight of all beans in `buffer` having `desired_color`

   number: Natural := 0;
   --  number of beans in `buffer` having `desired_color`

   procedure accumulate(c: Cursor) is
      --  if the element at `c` has the `desired_color`, measure it
   begin
      if Element(c).kind = desired_color then
         number := number + 1;
         total := total + Element(c).weight;
      end if;
   end accumulate;

begin --  average_weight

   Iterate(buffer, accumulate'Access);

   if number > 0 then
      return total / number;
   else
      return 0.0;
   end if;

end average_weight;

這種方法很簡單。但是,想象一下更大的向量。average_weight將反覆訪問所有元素以獲取每種顏色。如果有 M 種顏色和 N 個豆子,average_weight將被呼叫 M * N 次,並且每新增一種新顏色,都需要呼叫 N 次。一種可能的選擇是在訪問每個豆子時收集有關該豆子的所有資訊。但是,這可能需要更多變數,並且您必須找到一種方法來返回多個結果(每種顏色的一個平均值)等。試試看!

可能另一種方法會更好。一種方法是將不同顏色的豆子複製到單獨的向量物件中。(記住灰姑娘。)然後average_weight必須只訪問每個元素一次。以下過程執行此操作,使用來自Beans的新型別,稱為Bean_Pots.

   ...
   type Bean_Pots is array(Bean_Color) of Bean_Vecs.Vector;
   ...

請注意,此普通陣列如何將顏色與向量相關聯。將豆子放入正確碗中的過程使用豆子顏色作為陣列索引來查詢正確的碗(向量)。

檔案:2/gather_into_pots.adb (檢視純文字下載頁面瀏覽所有)
procedure gather_into_pots(buffer: Bean_Vecs.Vector; pots: in out Bean_Pots) is
   use Bean_Vecs;

   procedure put_into_right_pot(c: Cursor) is
      --  select the proper bowl for the bean at `c` and «append»
      --  the bean to the selected bowl
   begin
      Append(pots(Element(c).kind), Element(c));
   end put_into_right_pot;

begin  --  gather_into_pots
   Iterate(buffer, put_into_right_pot'Access);
end gather_into_pots;


現在一切就緒了。

檔案: 2/bean_counting.adb (檢視, 純文字, 下載頁面, 瀏覽所有)
with Beans;
with average_weight;
with gather_into_pots;
with Ada.Wide_Text_IO;

procedure bean_counting is
   use Beans, Ada;

   buffer: Bean_Vecs.Vector;
   bowls: Bean_Pots;

   procedure read_input(buf: in out Bean_Vecs.Vector) is separate;
   --  collect information from a series of bean measurements into `buf`


begin --  bean_counting

   read_input(buffer);

   --  now everything is set up for computing some statistical data.
   --  Gather the beans into the right pot by color.
   --  Then find the average weight of beans in each pot.

   gather_into_pots(buffer, bowls);

   for color in Bean_Color loop
      Wide_Text_IO.Put_Line
        (Bean_Color'Wide_Image(color)
         & " ø ="
         & Grams'Wide_Image(average_weight(bowls(color), color)));
   end loop;

end bean_counting;


由於我們為每種顏色選擇了一個向量,因此可以透過呼叫Length函式來確定每個向量中豆子的數量。但是average_weight也計算向量中的元素數量。因此,一個求和函式可以替換average_weight此處。

只需一張對映表!

[編輯 | 編輯原始碼]

以下程式首先呼叫read_input用於填充一個包含豆子的緩衝區。然後,有關這些豆子的資訊儲存在一個表中,該表將豆子的屬性對映到出現的次數。從Iterate開始的處理使用了Ada.Containers迭代機制中常見的鏈式過程呼叫。

此示例中的 Beans 包例項化了另一個泛型庫單元,Ada.Containers.Ordered_Maps。其中Ada.Containers.Hashed_Maps 需要雜湊函式,Ada.Containers.Ordered_Maps 需要比較函式。我們提供了一個函式,"<",它首先按顏色排序豆子,然後按重量排序。由於其名稱"<"與泛型形式函式"<".

   ...
   function "<"(a, b: Bean) return Boolean;
   --  order beans, first by color, then by weight

   package Bean_Statistics
     --  instances will map beans of a particular color and weight to the
     --  number of times they have been inserted.
   is new Ada.Containers.Ordered_Maps
     (Element_Type => Natural,
      Key_Type => Bean);
   ...


的名稱匹配,因此它將自動與相應的泛型形式函式關聯。在前面的示例中,我們使用了bean_counting的變體,將所有子程式打包為區域性子程式。

檔案: 3/bean_counting.adb (檢視, 純文字, 下載頁面, 瀏覽所有)
with Beans;
with Ada.Wide_Text_IO;

procedure bean_counting is
   use Beans, Ada;

   buffer: Bean_Vecs.Vector;
   stats_cw: Bean_Statistics.Map;
   --  maps beans to numbers of occurrences, grouped by color, ordered by
   --  weight

   procedure read_input(buf: in out Bean_Vecs.Vector) is separate;
   --  collect information from a series of bean measurements into `buf`

   procedure add_bean_info(specimen: in Bean);
   --  insert bean `specimen` as a key into the `stats_cw` table unless
   --  present. In any case, increase the count associated with this key
   --  by 1. That is, count the number of equal beans.

   procedure add_bean_info(specimen: in Bean) is

      procedure one_more(b: in Bean; n: in out Natural) is
       --   increase the count associated with this kind of bean
      begin
         n := n + 1;
      end one_more;

      c : Bean_Statistics.Cursor;
      inserted: Boolean;
   begin
      stats_cw.Insert(specimen, 0, c, inserted);
      Bean_Statistics.Update_Element(c, one_more'Access);
   end add_bean_info;

begin --  bean_counting

   read_input(buffer);

   --  next, for all beans in the vector `buffer` just filled, store
   --  information about each bean in the `stats_cw` table.

   declare
      use Bean_Vecs;

      procedure count_bean(c: Cursor) is
      begin
         add_bean_info(Element(c));
      end count_bean;
   begin
      Iterate(buffer, count_bean'Access);
   end;

   --  now everything is set up for computing some statistical data. The
   --  keys of the map, i.e. beans, are ordered by color and then weight.
   --  The `First`, and `Ceiling` functions will find cursors
   --  denoting the ends of a group.


   declare
      use Bean_Statistics;

      --  statistics is computed groupwise:

      q_sum: Grams;
      q_count: Natural;

      procedure q_stats(lo, hi: Cursor);
      --  `q_stats` will update the `q_sum` and `q_count` globals with
      --  the sum of the key weights and their number, respectively. `lo`
      --  (included) and `hi` (excluded)  mark the interval of keys
      --  to use from the map.

      procedure q_stats(lo, hi: Cursor) is
         k: Cursor := lo;
      begin
         q_count := 0; q_sum := 0.0;
         loop
            exit when k = hi;
            q_count := q_count + Element(k);
            q_sum := q_sum + Key(k).weight * Element(k);
            Next(k);
         end loop;
      end q_stats;


      --  precondition
      pragma assert(not Is_Empty(stats_cw), "container is empty");

      lower, upper: Cursor := First(stats_cw);
      --  denoting the first key of a group, and the first key of a
      --  following group, respectively

   begin

      --  start reporting and trigger the computations

      Wide_Text_IO.Put_Line("Summary:");

      for color in Bean_Color loop
         lower := upper;
         if color = Bean_Color'Last then
            upper := No_Element;
         else
            upper := Ceiling(stats_cw, Bean'(Bean_Color'Succ(color), 0.0));
         end if;

         q_stats(lower, upper);

         if q_count > 0 then
            Wide_Text_IO.Put_Line
              (Bean_Color'Wide_Image(color) & " group:" &
               "  ø =" & Grams'Wide_Image(q_sum / q_count) &
               ", # =" & Natural'Wide_Image(q_count) &
               ", Σ =" & Grams'Wide_Image(q_sum));
         end if;
      end loop;
   end;

end bean_counting;

就像在問候語示例中一樣,您可以從表中選擇值。這次,這些值表示具有某些屬性的豆子的出現次數。
stats_cw表按鍵排序,即按豆子的屬性排序。給定特定的屬性,您可以使用FloorCeiling函式來逼近表中最接近所需屬性的豆子。

現在很容易列印一個直方圖,顯示每種豆子出現的頻率。如果 N 是某種豆子的數量,則在一行中列印 N 個字元,或繪製長度為 N 的圖形條等。在計算這種顏色的豆子總和後,可以使用前面示例中的組繪製顯示每種顏色豆子數量的直方圖。您可以使用相同的技術從表中刪除某種顏色的豆子。

最後,考慮按順序排列豆子,從出現頻率最低的種類開始。也就是說,構造一個向量,首先附加僅出現一次的豆子,然後附加出現兩次的豆子(如果有),依此類推。從表開始是可能的,但請務必檢視Ada.Containers的排序函式。

另請參閱

[編輯 | 編輯原始碼]

華夏公益教科書

[編輯 | 編輯原始碼]

Ada 2005 參考手冊

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