Ada 编程/容器
以下是某些容器类型的简单演示。它没有涵盖所有内容,但应该可以帮助您入门。
此语言功能仅从Ada 2005开始可用。
下面的程序用多种人类语言向世界问好。问候语存储在一个表格或哈希映射中。该映射将每个问候语(一个值)与语言代码(一个键)相关联。也就是说,您可以使用语言代码作为键在表格中查找问候语值。
映射中的元素是国际字符的常量字符串,或者更确切地说,是指向此类常量字符串的指针。一个包区域用于设置语言 ID 和Ada.Containers.Hashed_Maps的一个实例。
with
Ada.Containers.Hashed_Maps;use
Ada.Containers;package
Regionalis
type
Language_IDis
(DE, EL, EN, ES, FR, NL); -- a selection from the two-letter codes for human languagestype
Hello_Textis
access
constant
Wide_String; -- objects will contain a «hello»-string in some languagefunction
ID_Hashed (id: Language_ID)return
Hash_Type; -- you need to provide this to every hashed containerpackage
Phrasesis
new
Ada.Containers.Hashed_Maps (Key_Type => Language_ID, Element_Type => Hello_Text, Hash => ID_Hashed, Equivalent_Keys => "=");end
Regional;
这是程序,稍后将解释详细信息。
with
Regional;use
Regional;with
Ada.Wide_Text_IO;use
Ada;procedure
Hello_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 =>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,然后迭代以下调用
- Has_Element,用于检查光标是否位于元素上
- Element,以获取元素和
- Next,以将光标移动到另一个元素
当没有更多元素剩余时,光标将具有特殊值No_Element。实际上,这是一种可以与Ada.Containers子包中的所有容器一起使用的迭代方案。
下一个程序演示了如何根据键从映射中选择一个值。实际上,您将提供键。该程序与前面的程序类似,只是它不仅打印映射中的所有元素,而且根据从标准输入读取的 Language_ID 值选择一个元素。
with
Regional;use
Regional;with
Ada.Wide_Text_IO;use
Ada;procedure
Hello_World_Pickis
... as before ...declare
use
Phrases;package
Lang_IOis
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 类型不属于蔬菜包,但它在那里是为了简化操作)
with
Ada.Containers.Vectors;package
Beansis
type
Bean_Coloris
(R, G, W); -- red, green, and white beanstype
Gramsis
delta
0.01digits
7; -- enough to weigh things as light as beans but also as heavy as -- many of themtype
Beanis
-- info about a single beanrecord
kind: Bean_Color; weight: Grams;end
record
;subtype
Bean_Countis
Positiverange
1 .. 1_000; -- numbers of beans to count (how many does Cinderella have to count?)package
Bean_Vecsis
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
然后打印每种颜色的豆子的平均值,程序停止。
with
Beans;with
average_weight;with
Ada.Wide_Text_IO;procedure
bean_countingis
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
kindin
Bean_Colorloop
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示例中所做的那样。
with
Beans;use
Beans.Bean_Vecs;function
average_weight (buffer: Bean_Vecs.Vector; desired_color: Bean_Color)return
Gramsis
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 itbegin
if
Element(c).kind = desired_colorthen
number := number + 1; total := total + Element(c).weight;end
if
;end
accumulate;begin
-- average_weight Iterate(buffer, accumulate'Access);if
number > 0then
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_Potsis
array
(Bean_Color)of
Bean_Vecs.Vector; ...
请注意,此普通数组如何将颜色与向量相关联。将豆子放入正确碗中的过程使用豆子颜色作为数组索引来查找正确的碗(向量)。
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 bowlbegin
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;
现在一切就绪了。
with
Beans;with
average_weight;with
gather_into_pots;with
Ada.Wide_Text_IO;procedure
bean_countingis
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
colorin
Bean_Colorloop
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 weightpackage
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的变体,将所有子程序打包为局部子程序。
with
Beans;with
Ada.Wide_Text_IO;procedure
bean_countingis
use
Beans, Ada; buffer: Bean_Vecs.Vector; stats_cw: Bean_Statistics.Map; -- maps beans to numbers of occurrences, grouped by color, ordered by -- weightprocedure
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 beanbegin
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; -- preconditionpragma
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, respectivelybegin
-- start reporting and trigger the computations Wide_Text_IO.Put_Line("Summary:");for
colorin
Bean_Colorloop
lower := upper;if
color = Bean_Color'Lastthen
upper := No_Element;else
upper := Ceiling(stats_cw, Bean'(Bean_Color'Succ(color), 0.0));end
if
; q_stats(lower, upper);if
q_count > 0then
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表按键排序,即按豆子的属性排序。给定特定的属性,您可以使用Floor和Ceiling函数来逼近表中最接近所需属性的豆子。
现在很容易打印一个直方图,显示每种豆子出现的频率。如果 N 是某种豆子的数量,则在一行中打印 N 个字符,或绘制长度为 N 的图形条等。在计算这种颜色的豆子总和后,可以使用前面示例中的组绘制显示每种颜色豆子数量的直方图。您可以使用相同的技术从表中删除某种颜色的豆子。
最后,考虑按顺序排列豆子,从出现频率最低的种类开始。也就是说,构造一个向量,首先附加仅出现一次的豆子,然后附加出现两次的豆子(如果有),依此类推。从表开始是可能的,但请务必查看Ada.Containers的排序函数。