Ada 程式設計/容器
以下是某些容器型別的簡單演示。它沒有涵蓋所有內容,但應該可以幫助您入門。
此語言功能僅從Ada 2005開始可用。
下面的程式用多種人類語言向世界問好。問候語儲存在一個表格或雜湊對映中。該對映將每個問候語(一個值)與語言程式碼(一個鍵)相關聯。也就是說,您可以使用語言程式碼作為鍵在表格中查詢問候語值。
對映中的元素是國際字元的常量字串,或者更確切地說,是指向此類常量字串的指標。一個包區域用於設定語言 ID 和Ada.Containers.Hashed_Maps的一個例項。
withAda.Containers.Hashed_Maps;useAda.Containers;packageRegionalistypeLanguage_IDis(DE, EL, EN, ES, FR, NL); -- a selection from the two-letter codes for human languagestypeHello_TextisaccessconstantWide_String; -- objects will contain a «hello»-string in some languagefunctionID_Hashed (id: Language_ID)returnHash_Type; -- you need to provide this to every hashed containerpackagePhrasesisnewAda.Containers.Hashed_Maps (Key_Type => Language_ID, Element_Type => Hello_Text, Hash => ID_Hashed, Equivalent_Keys => "=");endRegional;
這是程式,稍後將解釋詳細資訊。
withRegional;useRegional;withAda.Wide_Text_IO;useAda;procedureHello_World_Extendedis-- print greetings in different spoken languages greetings: Phrases.Map; -- the dictionary of greetingsbegin-- Hello_World_Extended Phrases.Insert(greetings, Key => EN, New_Item =>newWide_String'("Hello, World!")); -- or, shorter, greetings.Insert(DE,newWide_String'("Hallo, Welt!")); greetings.Insert(NL,newWide_String'("Hallo, Wereld!")); greetings.Insert(ES,newWide_String'("¡Hola mundo!")); greetings.Insert(FR,newWide_String'("Bonjour, Monde!")); greetings.Insert(EL,newWide_String'("Γεια σου κόσμε"));declareusePhrases; speaker: Cursor := First(greetings);beginwhileHas_Element(speaker)loopWide_Text_IO.Put_Line( Element(speaker).all); Next(speaker);endloop;end;endHello_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,然後迭代以下呼叫
- Has_Element,用於檢查游標是否位於元素上
- Element,以獲取元素和
- Next,以將游標移動到另一個元素
當沒有更多元素剩餘時,游標將具有特殊值No_Element。實際上,這是一種可以與Ada.Containers子包中的所有容器一起使用的迭代方案。
下一個程式演示瞭如何根據鍵從對映中選擇一個值。實際上,您將提供鍵。該程式與前面的程式類似,只是它不僅列印對映中的所有元素,而且根據從標準輸入讀取的 Language_ID 值選擇一個元素。
withRegional;useRegional;withAda.Wide_Text_IO;useAda;procedureHello_World_Pickis... as before ...declareusePhrases;packageLang_IOisnewWide_Text_IO.Enumeration_IO(Language_ID); lang: Language_ID;beginLang_IO.Get(lang); Wide_Text_IO.Put_Line( greetings.Element(lang).all);end;endHello_World_Pick;
這次的Element函式使用一個鍵 (lang) 而不是一個游標。實際上,它使用兩個值,另一個值是greetings,以區分接收者表示法。
讓我們從字面上理解豆子計數。紅豆、綠豆和白豆。(是的,白豆確實存在。)您的工作是收集一定數量的豆子,稱重,然後分別確定紅豆、綠豆和白豆的平均重量。這是一種方法。
同樣,我們需要一個包,這次用於儲存與蔬菜相關的資訊。介紹一下Beans包(Grams 型別不屬於蔬菜包,但它在那裡是為了簡化操作)
withAda.Containers.Vectors;packageBeansistypeBean_Coloris(R, G, W); -- red, green, and white beanstypeGramsisdelta0.01digits7; -- enough to weigh things as light as beans but also as heavy as -- many of themtypeBeanis-- info about a single beanrecordkind: Bean_Color; weight: Grams;endrecord;subtypeBean_CountisPositiverange1 .. 1_000; -- numbers of beans to count (how many does Cinderella have to count?)packageBean_VecsisnewAda.Containers.Vectors (Element_Type => Bean, Index_Type => Bean_Count);endBeans;
該Vectors例項提供了一個類似於陣列的資料結構,該資料結構可以在執行時更改其大小。它被稱為Vector。讀取的每個豆子都將附加到一個Bean_Vecs.Vector物件。
以下程式首先呼叫read_input以用豆子填充緩衝區。接下來,它呼叫一個函式,該函式計算具有相同顏色的豆子的平均重量。此函式
withBeans;useBeans;functionaverage_weight (buffer: Bean_Vecs.Vector; desired_color: Bean_Color)returnGrams; -- scan `buffer` for all beans that have `desired_color`. Compute the -- mean of their `.weight` components
然後列印每種顏色的豆子的平均值,程式停止。
withBeans;withaverage_weight;withAda.Wide_Text_IO;procedurebean_countingisuseBeans, Ada; buffer: Bean_Vecs.Vector;procedureread_input(buf:inoutBean_Vecs.Vector)isseparate; -- 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.forkindinBean_ColorloopWide_Text_IO.Put_Line (Bean_Color'Wide_Image(kind) & " ø =" & Grams'Wide_Image( average_weight(buffer, kind) ));endloop;endbean_counting;
所有容器操作都在函式average_weight中進行。為了找到相同顏色的豆子的平均重量,該函式正在按順序檢視所有豆子。如果一個豆子具有正確的顏色,average_weight將它的重量新增到總重量中,並將計數的豆子數量增加 1。
計算訪問所有豆子。從一個豆子移動到下一個豆子,然後執行上述步驟所需的迭代最好留給Iterate過程,它是所有容器包的一部分。為此,將上述步驟包裝在某個過程中,並將此過程傳遞給Iterate。效果是Iterate為向量中的每個元素呼叫您的過程,並將游標值傳遞給您的過程,每個元素一個。
讓容器機制執行迭代也可能比自己移動和檢查游標更快,就像在Hello_World_Extended示例中所做的那樣。
withBeans;useBeans.Bean_Vecs;functionaverage_weight (buffer: Bean_Vecs.Vector; desired_color: Bean_Color)returnGramsistotal: Grams := 0.0; -- weight of all beans in `buffer` having `desired_color` number: Natural := 0; -- number of beans in `buffer` having `desired_color`procedureaccumulate(c: Cursor)is-- if the element at `c` has the `desired_color`, measure itbeginifElement(c).kind = desired_colorthennumber := number + 1; total := total + Element(c).weight;endif;endaccumulate;begin-- average_weight Iterate(buffer, accumulate'Access);ifnumber > 0thenreturntotal / number;elsereturn0.0;endif;endaverage_weight;
這種方法很簡單。但是,想象一下更大的向量。average_weight將反覆訪問所有元素以獲取每種顏色。如果有 M 種顏色和 N 個豆子,average_weight將被呼叫 M * N 次,並且每新增一種新顏色,都需要呼叫 N 次。一種可能的選擇是在訪問每個豆子時收集有關該豆子的所有資訊。但是,這可能需要更多變數,並且您必須找到一種方法來返回多個結果(每種顏色的一個平均值)等。試試看!
可能另一種方法會更好。一種方法是將不同顏色的豆子複製到單獨的向量物件中。(記住灰姑娘。)然後average_weight必須只訪問每個元素一次。以下過程執行此操作,使用來自Beans的新型別,稱為Bean_Pots.
...typeBean_Potsisarray(Bean_Color)ofBean_Vecs.Vector; ...
請注意,此普通陣列如何將顏色與向量相關聯。將豆子放入正確碗中的過程使用豆子顏色作為陣列索引來查詢正確的碗(向量)。
proceduregather_into_pots(buffer: Bean_Vecs.Vector; pots:inoutBean_Pots)isuseBean_Vecs;procedureput_into_right_pot(c: Cursor)is-- select the proper bowl for the bean at `c` and «append» -- the bean to the selected bowlbeginAppend(pots(Element(c).kind), Element(c));endput_into_right_pot;begin-- gather_into_pots Iterate(buffer, put_into_right_pot'Access);endgather_into_pots;
現在一切就緒了。
withBeans;withaverage_weight;withgather_into_pots;withAda.Wide_Text_IO;procedurebean_countingisuseBeans, Ada; buffer: Bean_Vecs.Vector; bowls: Bean_Pots;procedureread_input(buf:inoutBean_Vecs.Vector)isseparate; -- 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);forcolorinBean_ColorloopWide_Text_IO.Put_Line (Bean_Color'Wide_Image(color) & " ø =" & Grams'Wide_Image(average_weight(bowls(color), color)));endloop;endbean_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)returnBoolean; -- order beans, first by color, then by weightpackageBean_Statistics -- instances will map beans of a particular color and weight to the -- number of times they have been inserted.isnewAda.Containers.Ordered_Maps (Element_Type => Natural, Key_Type => Bean); ...
的名稱匹配,因此它將自動與相應的泛型形式函式關聯。在前面的示例中,我們使用了bean_counting的變體,將所有子程式打包為區域性子程式。
withBeans;withAda.Wide_Text_IO;procedurebean_countingisuseBeans, Ada; buffer: Bean_Vecs.Vector; stats_cw: Bean_Statistics.Map; -- maps beans to numbers of occurrences, grouped by color, ordered by -- weightprocedureread_input(buf:inoutBean_Vecs.Vector)isseparate; -- collect information from a series of bean measurements into `buf`procedureadd_bean_info(specimen:inBean); -- 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.procedureadd_bean_info(specimen:inBean)isprocedureone_more(b:inBean; n:inoutNatural)is-- increase the count associated with this kind of beanbeginn := n + 1;endone_more; c : Bean_Statistics.Cursor; inserted: Boolean;beginstats_cw.Insert(specimen, 0, c, inserted); Bean_Statistics.Update_Element(c, one_more'Access);endadd_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.declareuseBean_Vecs;procedurecount_bean(c: Cursor)isbeginadd_bean_info(Element(c));endcount_bean;beginIterate(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.declareuseBean_Statistics; -- statistics is computed groupwise: q_sum: Grams; q_count: Natural;procedureq_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.procedureq_stats(lo, hi: Cursor)isk: Cursor := lo;beginq_count := 0; q_sum := 0.0;loopexitwhenk = hi; q_count := q_count + Element(k); q_sum := q_sum + Key(k).weight * Element(k); Next(k);endloop;endq_stats; -- preconditionpragmaassert(notIs_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, respectivelybegin-- start reporting and trigger the computations Wide_Text_IO.Put_Line("Summary:");forcolorinBean_Colorlooplower := upper;ifcolor = Bean_Color'Lastthenupper := No_Element;elseupper := Ceiling(stats_cw, Bean'(Bean_Color'Succ(color), 0.0));endif; q_stats(lower, upper);ifq_count > 0thenWide_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));endif;endloop;end;endbean_counting;
就像在問候語示例中一樣,您可以從表中選擇值。這次,這些值表示具有某些屬性的豆子的出現次數。
stats_cw表按鍵排序,即按豆子的屬性排序。給定特定的屬性,您可以使用Floor和Ceiling函式來逼近表中最接近所需屬性的豆子。
現在很容易列印一個直方圖,顯示每種豆子出現的頻率。如果 N 是某種豆子的數量,則在一行中列印 N 個字元,或繪製長度為 N 的圖形條等。在計算這種顏色的豆子總和後,可以使用前面示例中的組繪製顯示每種顏色豆子數量的直方圖。您可以使用相同的技術從表中刪除某種顏色的豆子。
最後,考慮按順序排列豆子,從出現頻率最低的種類開始。也就是說,構造一個向量,首先附加僅出現一次的豆子,然後附加出現兩次的豆子(如果有),依此類推。從表開始是可能的,但請務必檢視Ada.Containers的排序函式。
