Ada 程式設計/庫/Ada.Containers.Vectors
此語言功能僅從 Ada 2005 開始可用。
Ada.Containers.Vectors 是從 Ada 2005 開始的 預定義語言環境 的一個單元。
此泛型包提供了一個容器(Vector),它可以在連續列表中儲存任何 確定子型別。這使得一個 Ada.Containers.Vectors.Vector 類似於陣列——然而,向量可以在聲明後改變大小,而陣列則不能。因此,向量也被稱為動態陣列或可調整大小的陣列。
Ada 2005 的主要新增功能之一是容器庫。此庫使 Ada 開發人員能夠操作資料結構,例如雙向連結串列、對映、集合和向量。本頁將展示 Ada.Containers.Vectors 包的工作方式。
但首先:什麼是向量?以下是參考手冊對它的解釋
語言定義的泛型包 Containers.Vectors 提供了私有型別 Vector 和 Cursor,以及針對每種型別的操作集。向量容器允許在任何位置進行插入和刪除,但它專門針對容器的高階(具有較高索引的端)進行插入和刪除進行了最佳化。向量容器還提供了對其元素的隨機訪問。
向量容器在概念上表現得像一個數組,當插入專案時,它會根據需要擴充套件。向量的長度是向量包含的元素數量。向量的容量是可以在向量自動擴充套件之前插入到向量的最大元素數量。
向量容器中的元素可以透過泛型形式型別的一個索引值來引用。向量的第一個元素的索引值始終等於形式型別的下限。
向量容器可能包含空元素。空元素沒有指定的值。
基本上它是一個一維陣列,在很多方面它的行為與陣列一樣,允許對向量元素進行隨機和順序訪問。主要區別在於,普通陣列的大小是固定的,而向量則不是,只要有足夠的資源來容納向量。向量在新增新元素時會自動擴充套件。
向量的靈活性是一件很棒的事情,但它確實有代價:向量不像普通陣列那樣快和輕便。向量決不慢或佔用太多資源,但它們比普通陣列更慢更重。另一方面,如果你能忍受更高的資源需求,向量確實為你提供了一套非常直觀的工具來管理你的資料列表。
學習如何使用 Ada.Containers.Vectors 庫,在我看來,最好是用實際的、可執行的程式碼來完成。我不太喜歡虛擬碼,所以本頁的所有示例都將編譯並執行。
所有示例都圍繞著一個名為“Linus Torvalds 語錄”的小應用程式,在這個應用程式中,我們在一個普通的、扁平的文字檔案中有一堆語錄。該檔案 (quotes.txt) 將在所有示例中使用,它看起來像這樣
I have an ego the size of a small planet. My name is Linus Torvalds and I am your god. Do you pine for the days when men were men and wrote their own device drivers? If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. An infinite number of monkeys typing into GNU emacs would never make a good program. Is "I hope you all die a painful death" too strong? Most days I wake up thinking I'm the luckiest bastard alive. I'm always right. This time I'm just even more right than usual. And what's the internet without the rick-roll?
如果不明確,每行代表來自 Linus 的一句語錄。
我們的想法是,語錄可以隨時新增和刪除,因此程式在讀取所有語錄之前並不知道有多少語錄,這使得向量成為儲存和操作所有語錄的完美資料結構。
我們不會真正構建整個應用程式。我們只關注從 quotes.txt 檔案中讀取語錄,然後將其新增到向量中的部分。
在我們開始編寫實際程式碼之前,讓我們先看看 Ada.Containers.Vectors 規範的開頭
generic
type Index_Type is range <>;
type Element_Type is private;
請注意型別Index_Type 和 Element_Type。前者定義了向量的**索引**,後者定義了向量包含的**元素型別**。瞭解這一點後,我們來檢視一下基本的Quotes 程式本身。
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Containers.Vectors; use Ada.Containers;
procedure Quotes is
package IO renames Ada.Text_IO;
package SUIO renames Ada.Text_IO.Unbounded_IO;
package Quote_Container is new Vectors (Natural, Unbounded_String);
use Quote_Container;
Quotes : Vector;
Input : IO.File_Type;
begin
IO.Open (File => Input,
Mode => IO.In_File,
Name => "quotes.txt");
while not IO.End_Of_File (File => Input) loop
Quotes.Append (New_Item => SUIO.Get_Line (File => Input));
end loop;
IO.Close (Input);
end Quotes;
如果我們關注程式中與向量相關的部分,我們會發現第 4 行添加了 Ada.Containers.Vectors 庫單元。這一步對於使用向量至關重要,就像Ada.Text_IO對於基本字串 I/O 來說是必要的。如果您不熟悉這個概念,您應該先閱讀有關 Ada 基礎知識的內容,然後再回到這裡。
有了可用的向量庫,我們把注意力轉向第 9 行,在這裡我們用引數Natural 和 Unbounded_String 例項化了Vectors 泛型。結果是一個向量,其中索引從 0 開始(記住Natural 是Integer 的一個子型別,範圍為0 .. Integer'Last),元素的型別為Unbounded_String。在第 11 行,我們將Quotes 變數宣告為Vector 型別。由於第 10 行有use Quote_Container 子句,我們不需要完整的宣告Quote_Container.Vector。
現在我們有一個工作的向量Quotes,下一步自然是要向其中新增一些資料。這在第 18 行完成,使用Append 過程將每個引號追加到向量中。最後,我們關閉檔案並退出程式。
這個簡單的Quotes 程式是本文的基礎。下面幾乎所有的示例都是在這個程式的基礎上擴充套件的。但在繼續探討向量庫的各種過程和函式之前,讓我們簡要地看一下用於不確定型別的向量包。
Ada.Containers.Indefinite_Vectors
[edit | edit source]兄弟庫 Ada.Containers.Indefinite_Vectors 可用於不確定型別。它的工作方式與Ada.Containers.Vectors 非常相似,只是省略了一些Insert 過程。以下是一個關於它如何工作的示例。
with Ada.Text_IO;
with Ada.Containers.Indefinite_Vectors; use Ada.Containers;
procedure Quotes is
package IO renames Ada.Text_IO;
package Quote_Container is new Indefinite_Vectors (Natural, String);
use Quote_Container;
Quotes : Vector;
Input : IO.File_Type;
begin
IO.Open (File => Input,
Mode => IO.In_File,
Name => "quotes.txt");
while not IO.End_Of_File (File => Input) loop
Quotes.Append (New_Item => IO.Get_Line (File => Input));
end loop;
IO.Close (Input);
IO.Put_Line (Item => "No. of quotes:" & Quotes.Length'Img);
IO.Put_Line (Item => "Quote no. 2: " & Quotes.Element (Index => 1));
end Quotes;
編譯並執行這段程式碼,您將得到:
No. of quotes: 10 Quote no. 2: My name is Linus Torvalds and I am your god.
需要注意的是,不確定向量比約束向量慢,因此如果效能很重要,請避免使用它們。
由於確定性和不確定性容器非常相似,本文的其餘部分將只關注向量庫的確定性版本。
過程和函式
[edit | edit source]Ada.Containers.Vectors 包中包含**很多**過程和函式。幸運的是,它們的名字都很貼切,因此通常很容易確定在特定情況下需要哪一個過程或函式。為了進一步幫助您識別過程 X 或函式 Y 究竟是做什麼的,我將它們根據其主要功能分為幾組。
有些過程/函式可能屬於不止一類。在這些情況下,我將它們放在我認為它們最常用的類別中。
設定向量的尺寸/容量
[edit | edit source]在Ada.Containers.Vectors 包中,可以使用工具來建立指定尺寸/容量的向量。這些方法的主要目標是使程式設計師能夠分配所需的資源量,從而避免在向向量新增新元素時內部呼叫Reserve_Capacity 所產生的效能開銷。
Vectors.Reserve_Capacity
[edit | edit source]以下是Reserve_Capacity 的規範:
procedure Reserve_Capacity
(Container : in out Vector;
Capacity : Count_Type);
Ada 語言參考手冊對Reserve_Capacity 的解釋最為充分。
Reserve_Capacity分配新的內部資料結構,以便結果向量的長度至少可以達到Capacity的值,而無需額外呼叫Reserve_Capacity,並且足以容納Container的當前長度。然後,Reserve_Capacity將元素複製到新的資料結構中,並釋放舊的資料結構。在分配期間引發的任何異常都會被傳播,並且Container不會被修改。
實際上,只有在少數情況下使用Reserve_Capacity 才有意義,其中一些罕見的情況包括:
- 為特定尺寸的向量準備資源。
- 釋放過多的資源。
當然,可能還有其他原因,但這兩個原因可能是最常見的,其中第一個原因是絕對的首選。因此,讓我們在第 20 行IO.Close (Input); 呼叫之後,在Quotes 程式的主體中新增以下程式碼:
IO.Put_Line (Item => "Capacity:" & Quotes.Capacity'Img);
Quotes.Reserve_Capacity (Capacity => 100);
IO.Put_Line (Item => "Capacity:" & Quotes.Capacity'Img);
編譯並執行Quotes,您將得到:
Capacity: 16 Capacity: 100
讓我們嘗試擴充套件一下Quotes 程式,看看如何在程式執行過程中操作向量。將以下程式碼放在IO.Open 呼叫之前:
Quotes.Reserve_Capacity (Capacity => 100);
IO.Put_Line (Item => "Capacity:" & Quotes.Capacity'Img);
IO.Put_Line (Item => "Length:" & Quotes.Length'Img);
將以下程式碼放在IO.Close 呼叫之後:
IO.Put_Line (Item => "Capacity:" & Quotes.Capacity'Img);
IO.Put_Line (Item => "Length:" & Quotes.Length'Img);
Quotes.Reserve_Capacity (Capacity => Quotes.Length);
IO.Put_Line (Item => "Capacity:" & Quotes.Capacity'Img);
IO.Put_Line (Item => "Length:" & Quotes.Length'Img);
輸出如下:
Capacity: 100 Length: 0 Capacity: 100 Length: 10 Capacity: 10 Length: 10
如果您正在處理大型向量,那麼可能值得檢查一下,看是否可以透過一些精心放置的Reserve_Capacity 呼叫來加速程式碼或節省一些資源,但是,就像所有最佳化一樣:不要過早地進行最佳化,並且要嚴格地進行測試。
Vectors.Set_Length
[edit | edit source]以下是Length 的規範:
procedure Set_Length
(Container : in out Vector;
Length : Count_Type);
有人可能會問,像Set_Length 這樣的過程有什麼意義,因為向量在新增新元素時會自動增長,那麼為什麼還要使用它呢?為了回答這個問題,重要的是要理解向量的長度永遠不能超過其容量。
向量的容量是指為向量預留的內部資料結構的數量。這可能遠大於向量的實際長度,但永遠不會小於它(參見 Vectors.Capacity)。因此,當向向量新增新元素時,首先使用 Reserve_Capacity 擴充套件向量的容量(如果必要的話),然後新增新元素。因此,向向量中新增 100 個新元素,實際上會呼叫 Reserve_Capacity 100 次。這無疑是浪費資源,而使用Set_Length 正好可以避免這種浪費。如果我們知道要新增一堆新元素,我們可以將所有對 Reserve_Capacity 的呼叫累積到一個對Set_Length 的呼叫中。這樣效率更高。
Set_Length 的另一個用途是縮短向量,我們將在下面的示例中看到。
IO.Put_Line (Item => "Length of vector:" & Quotes.Length'Img);
Quotes.Set_Length (Length => 20);
IO.Put_Line (Item => "Length of vector:" & Quotes.Length'Img);
Quotes.Set_Length (Length => 5);
IO.Put_Line (Item => "Length of vector:" & Quotes.Length'Img);
輸出如下:
Length of vector: 10 Length of vector: 20 Length of vector: 5
為了展示使用Set_Length 的好處,我編寫了兩個小程式。第一個程式使用Append 添加了 10_000_000 個新元素。第二個程式也這樣做,但它使用的是Set_Length 和Replace_Element。
with Ada.Text_IO;
with Ada.Containers.Vectors; use Ada.Containers;
procedure Quotes is
package IO renames Ada.Text_IO;
package Number_Container is new Vectors (Natural, Natural);
Numbers : Number_Container.Vector;
begin
IO.Put_Line ("Length:" & Numbers.Length'Img);
for i in 0 .. 9999999 loop
Numbers.Append (New_Item => i);
end loop;
IO.Put_Line ("Length:" & Numbers.Length'Img);
end Quotes;
以下是在我的電腦上計時後的結果:
Length: 0 Length: 10000000 real 0m0.438s user 0m0.336s sys 0m0.096s
現在,讓我們藉助Set_Length 重寫這個小巧的程式,並重新計時。
with Ada.Text_IO;
with Ada.Containers.Vectors; use Ada.Containers;
procedure Quotes is
package IO renames Ada.Text_IO;
package Number_Container is new Vectors (Natural, Natural);
Numbers : Number_Container.Vector;
begin
IO.Put_Line ("Length:" & Numbers.Length'Img);
Numbers.Set_Length (Length => 10000000);
for i in 0 .. 9999999 loop
Numbers.Replace_Element (Index => i,
New_Item => i);
end loop;
IO.Put_Line ("Length:" & Numbers.Length'Img);
end Quotes;
以下是這個版本的結果:
Length: 0 Length: 10000000 real 0m0.109s user 0m0.064s sys 0m0.044s
這在執行時間上有了巨大的改進,因此,如果需要向向量中新增大量新元素,請務必使用Set_Length。
以上結果是 10 次執行的平均值。
Vectors.To_Vector
[edit | edit source]To_Vector 的規範如下所示:
function To_Vector (Length : Count_Type) return Vector;
function To_Vector
(New_Item : Element_Type;
Length : Count_Type) return Vector;
這兩個To_Vector 函式的作用應該很明顯。我將把這兩個函式的示例放在一起,因為它們非常相似。將以下程式碼新增到Quotes 程式的主體中:
IO.Put (Item => "1st. element: ");
SUIO.Put (Item => Quotes.Element (Index => 0));
IO.New_Line;
IO.Put (Item => "10th. element: ");
SUIO.Put_Line (Item => Quotes.Element (Index => 9));
Quotes := To_Vector (Length => 10);
IO.Put (Item => "1st. element: ");
SUIO.Put (Item => Quotes.Element (Index => 0));
IO.New_Line;
IO.Put (Item => "10th. element: ");
SUIO.Put_Line (Item => Quotes.Element (Index => 9));
Quotes := To_Vector (New_Item => To_Unbounded_String
("Put new quote here"),
Length => 10);
IO.Put (Item => "1st. element: ");
SUIO.Put (Item => Quotes.Element (Index => 0));
IO.New_Line;
IO.Put (Item => "10th. element: ");
SUIO.Put_Line (Item => Quotes.Element (Index => 9));
輸出如下:
1st. element: I have an ego the size of a small planet. 10th. element: And what's the internet without the rick-roll? 1st. element: 10th. element: 1st. element: Put new quote here 10th. element: Put new quote here
那麼我們學到了什麼呢?第一個To_Vector 函式建立一個新的向量,其中包含Length 個空元素,而第二個To_Vector 函式建立一個新的向量,其中包含Length 個元素,所有元素的值都為New_Item。
正如我們將在 稍後 學習的那樣,讀取空元素實際上是一個有界錯誤,因此第二個 To_Vector 可能最安全,因為你強制將一個有效的值放入每個元素。
將資料插入向量是使用向量的關鍵部分。空向量通常沒有那麼有趣,所以讓我們看看如何向其中新增一些資料。
Append 的規範如下所示
procedure Append
(Container : in out Vector;
New_Item : Element_Type;
Count : Count_Type := 1);
procedure Append
(Container : in out Vector;
New_Item : Vector);
Append 所做的是將元素或其他向量新增到向量的末尾。
如果我們在程式中新增一些輸出功能,我們可以看到它是如何工作的
SUIO.Put_Line (Item => Quotes.Element (9));
Element 函式將在後面詳細討論,但現在知道 Element 函式返回在給定索引 (Index_Type) 處找到的元素 (Element_Type) 就可以了,在本例中是索引 10。
執行程式應該得到以下輸出
And what's the internet without the rick-roll?
為了完全清楚 Append 的作用,讓我們將這三行新增到程式的主體中
-- Output the contents of index no. 9
SUIO.Put_Line (Item => Quotes.Element (9));
-- Append a new element to the vector
Quotes.Append (New_Item => To_Unbounded_String ("Test append"));
-- Output the contents of the index no. 10
SUIO.Put_Line (Item => Quotes.Element (10));
執行程式,現在得到以下輸出
And what's the internet without the rick-roll? Test append
檢視第一個 Append 過程的規範,你可能已經注意到可選的 Count 引數。此引數的功能與其名稱密切相關,因為它使你能夠將元素插入 Count 次。讓我們看看它的實際應用。嘗試將其新增到 Quotes 程式的主體中
SUIO.Put_Line (Item => Quotes.Element (9));
-- Append the 3 identical elements to the vector
Quotes.Append (New_Item => To_Unbounded_String ("Test append"),
Count => 3);
SUIO.Put_Line (Item => Quotes.Element (10));
SUIO.Put_Line (Item => Quotes.Element (11));
SUIO.Put_Line (Item => Quotes.Element (12));
輸出如下:
And what's the internet without the rick-roll? Test append Test append Test append
在上面的示例中,我們使用了 Count 值 3,並且正如預期的那樣,元素“Test append”被追加到向量三次。
Vectors 包中的第二個 Append 過程將一個向量追加到另一個向量。要檢視它是如何工作的,我們需要建立一個新的向量,這需要對程式的宣告部分進行新增。新增以下宣告
My_Quotes : Vector;
我們剛剛做的是宣告一個新的向量 My_Quotes。這是我們將追加到 Quotes 向量的向量。
現在將其新增到程式的主體中
My_Quotes.Append (New_Item => To_Unbounded_String ("My 1st. quote"));
My_Quotes.Append (New_Item => To_Unbounded_String ("My 2nd. quote"));
My_Quotes.Append (New_Item => To_Unbounded_String ("My 3rd. quote"));
SUIO.Put_Line (Item => Quotes.Element (9));
SUIO.Put_Line (Item => My_Quotes.Element (0));
-- Append the My_Quotes vector to the Quotes vector
Quotes.Append (New_Item => My_Quotes);
SUIO.Put_Line (Item => Quotes.Element (12));
執行此程式的輸出是
And what's the internet without the rick-roll? My 1st. quote My 3rd. quote
請注意,我們使用的方法與追加常規元素時相同,只是在追加向量的 Append 過程中沒有使用 Count 引數。
關於 Append 過程沒有太多可說的了,所以讓我們繼續學習它的兄弟:Prepend。
Prepend 的規範如下所示
procedure Prepend
(Container : in out Vector;
New_Item : Vector);
procedure Prepend
(Container : in out Vector;
New_Item : Element_Type;
Count : Count_Type := 1);
Prepend 與 Append 非常相似,唯一的區別是資料插入到向量的開頭而不是末尾。要檢視它的實際應用,請將以下內容新增到基本 Quotes 程式的主體中
SUIO.Put_Line (Item => Quotes.Element (0));
-- Prepend a new element to the Quotes vector
Quotes.Prepend (New_Item => To_Unbounded_String ("Prepended!"));
SUIO.Put_Line (Item => Quotes.Element (0));
SUIO.Put_Line (Item => Quotes.Element (1));
執行程式的結果,正如預期的那樣
I have an ego the size of a small planet. Prepended! I have an ego the size of a small planet.
正如你所見,“Prepended!” 文字已新增到 Linus 的自戀語錄之前。
Prepend 也可以用來連線兩個向量。功能相同,唯一的區別是複製的是一個向量而不是單個元素。
Insert 的規範如下所示
procedure Insert
(Container : in out Vector;
Before : Extended_Index;
New_Item : Vector);
procedure Insert
(Container : in out Vector;
Before : Cursor;
New_Item : Vector);
procedure Insert
(Container : in out Vector;
Before : Cursor;
New_Item : Vector;
Position : out Cursor);
procedure Insert
(Container : in out Vector;
Before : Extended_Index;
New_Item : Element_Type;
Count : Count_Type := 1);
procedure Insert
(Container : in out Vector;
Before : Cursor;
New_Item : Element_Type;
Count : Count_Type := 1);
procedure Insert
(Container : in out Vector;
Before : Cursor;
New_Item : Element_Type;
Position : out Cursor;
Count : Count_Type := 1);
procedure Insert
(Container : in out Vector;
Before : Extended_Index;
Count : Count_Type := 1);
procedure Insert
(Container : in out Vector;
Before : Cursor;
Position : out Cursor;
Count : Count_Type := 1);
Insert 使我們能夠在列表中的任何位置插入一個或多個相同的元素。它還向我們介紹了一個新概念:遊標。
但讓我們先從使用索引來識別單個元素的 Insert 過程開始。由於它們非常相似,因此我將它們全部放在同一個示例中。將以下內容新增到 Quotes 程式的主體中
-- Insert an element at the second index of the vector
Quotes.Insert (Before => 1,
New_Item => To_Unbounded_String ("New second index!"));
-- Insert two identical elements at the fifth index of the vector
Quotes.Insert (Before => 4,
New_Item => To_Unbounded_String ("Two new elements @ 4 and 5!"),
Count => 2);
-- Insert two empty elements before the last index of the vector
Quotes.Insert (Before => Quotes.Last_Index,
Count => 2);
for i in Quotes.First_Index .. Quotes.Last_Index loop
SUIO.Put_Line (Item => i'Img & " " & Quotes.Element (Integer (i)));
end loop;
此程式的輸出是
0 I have an ego the size of a small planet. 1 New second index! 2 My name is Linus Torvalds and I am your god. 3 Do you pine for the days when men were men and wrote their own device drivers? 4 Two new elements @ 4 and 5! 5 Two new elements @ 4 and 5! 6 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 7 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 8 An infinite number of monkeys typing into GNU emacs would never make a good program. 9 Is "I hope you all die a painful death" too strong? 10 Most days I wake up thinking I'm the luckiest bastard alive. 11 I'm always right. This time I'm just even more right than usual. 12 13 14 And what's the internet without the rick-roll?
請注意,Insert 的最後一個呼叫不會插入兩個空元素。相反,會插入兩個向量預設值的元素。據我所知,此值目前為空,但我不會依賴它。如果你需要插入空元素,請檢視下面的 Insert_Space 過程。
透過 Insert,你可以將新元素插入向量的任何位置,只需適當地設定 Before 引數即可。向量將根據需要擴充套件,並且 Before 索引後的元素將被 Count 引數(其預設值為 1)的值向下移動。
剩餘的 Insert 過程都使用遊標。使用遊標,你可以同時訪問向量和元素。在下一個示例中,我將展示所有使用遊標的 Insert 過程。為了使用遊標,我們必須先宣告它,所以將以下內容新增到 Quotes 宣告中
Q_Cursor : Cursor;
並將以下內容新增到主體中
-- Move the cursor to the first position of the Quotes vector.
Q_Cursor := Quotes.First;
-- Insert an element at the first position of the vector.
Quotes.Insert (Before => Q_Cursor,
New_Item => To_Unbounded_String ("New index 0!"));
-- Move the cursor to the 5th. position of the vector
Q_Cursor := Quotes.To_Cursor (Index => 4);
-- Insert an element before the current 5th. position in the vector and
-- set the current position of the cursor.
Quotes.Insert (Before => Q_Cursor,
New_Item => To_Unbounded_String ("New index 4!"),
Position => Q_Cursor);
-- Move the cursor to the next position
Q_Cursor := Next (Position => Q_Cursor);
-- Insert two new elements before the current 6th. position in the vector.
Quotes.Insert (Before => Q_Cursor,
New_Item => To_Unbounded_String ("New index 5 and 6!"),
Count => 2);
-- Move the cursor to the 10th. position of the vector.
Q_Cursor := Quotes.To_Cursor (Index => 9);
-- Insert two elements before the 10th. position and set the current
-- position of the cursor.
Quotes.Insert (Before => Q_Cursor,
New_Item => To_Unbounded_String ("New index 9 and 10!"),
Position => Q_Cursor,
Count => 2);
-- Move the cursor to the last position of the vector.
Q_Cursor := Quotes.Last;
-- Insert two empty elements before the last position and set the position
-- of the cursor.
Quotes.Insert (Before => Q_Cursor,
Position => Q_Cursor,
Count => 2);
-- Move the cursor to the 1st. postion of the vector.
Q_Cursor := Quotes.First;
for i in Quotes.First_Index .. Quotes.Last_Index loop
-- Output the element of the current position.
SUIO.Put_Line (Item => To_Index (Position => Q_Cursor)'Img &
Element (Position => Q_Cursor));
-- Move the cursor to the next position in the vector.
Next (Position => Q_Cursor);
end loop;
執行程式的結果是
0New index 0! 1I have an ego the size of a small planet. 2My name is Linus Torvalds and I am your god. 3Do you pine for the days when men were men and wrote their own device drivers? 4New index 4! 5New index 5 and 6! 6New index 5 and 6! 7If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 8You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 9New index 9 and 10! 10New index 9 and 10! 11An infinite number of monkeys typing into GNU emacs would never make a good program. 12Is "I hope you all die a painful death" too strong? 13Most days I wake up thinking I'm the luckiest bastard alive. 14I'm always right. This time I'm just even more right than usual. 15 16 17And what's the internet without the rick-roll?
當我第一次編寫這段程式碼時,我很難想出使用遊標與 Ada.Containers.Vectors 庫的充分理由 - 對我來說,使用索引似乎更自然,更符合向量,但後來我瞭解到,實際上至少有兩個充分的理由可以使用遊標
- 它們使通用演算法成為可能
- 如果程式使用遊標建立,則可以輕鬆切換到其他序列容器
所以實際上至少有兩個充分的理由使用遊標。當然,如果你正在計算索引值,使用遊標不會給你帶來任何好處。像往常一樣,我們必須認真思考我們所需要的東西,並相應地選擇工具。
當你需要插入一堆“空”元素時,Insert_Space 就是完成這項工作的合適工具。Insert_Space 的規範如下所示
procedure Insert_Space
(Container : in out Vector;
Before : Extended_Index;
Count : Count_Type := 1);
procedure Insert_Space
(Container : in out Vector;
Before : Cursor;
Position : out Cursor;
Count : Count_Type := 1);
在使用 Insert_Space 時,需要注意的一點是,“空”元素並不真正意味著“空”。以下是參考手冊對此的說明
然後 Insert_Space 將 Before .. Last_Index (Container) 範圍內的元素向上滑動 Count 個位置,然後在從 Before 開始的位置插入空元素。
以及後面的
透過呼叫 Element、Query_Element、Update_Element、Swap、Is_Sorted、Sort、Merge、“=”、Find 或 Reverse_Find 來讀取空元素的值是一個有界錯誤。實現可能會將元素視為具有元素型別的任何正常值(參見 13.9.1),或者在修改向量之前引發 Constraint_Error 或 Program_Error。
從所有這一切中要帶走的重要一點是,“空”實際上意味著“不可預測的值”。這一切都取決於編譯器以及它如何處理新元素,因此不要指望 Insert_Space 傳遞乾淨整潔的空元素。它很可能只傳遞給你一堆已經存在元素的副本或其他一些值,只要它在向量的上下文中有效,在本例中意味著 Unbounded_String。還要注意,如果你嘗試對“空”元素使用 Element 函式,某些編譯器可能會引發 Constraint_Error 或 Program_Error。在我的情況下,我正在使用 GNATMAKE GPL 2008 (20080521),空元素被視為具有元素型別的任何正常值。
解決了這個問題,讓我們繼續看看 Insert_Space 在實際程式碼中的行為。首先將以下內容新增到 Quotes 程式的宣告部分
Q_Cursor : Cursor; -- Declare the cursor.
現在我們也可以使用 Insert_Space 的遊標版本。接下來,我們將以下內容新增到程式的主體中
-- Output all the quotes prior to any Insert_Space calls
for i in Quotes.First_Index .. Quotes.Last_Index loop
SUIO.Put_Line (Item => i'Img & " " & Quotes.Element (i));
end loop;
IO.New_Line (2);
-- Insert two "empty" elements at the beginning of the vector
Quotes.Insert_Space (Before => 0,
Count => 2);
-- Insert 3 "empty" elements before index 5 in the vector
Quotes.Insert_Space (Before => 5,
Count => 3);
-- Insert 3 "empty" elements before the last element, using a cursor.
Q_Cursor := Quotes.To_Cursor (Index => Quotes.Last_Index);
Quotes.Insert_Space (Before => Q_Cursor,
Position => Q_Cursor,
Count => 3);
for i in Quotes.First_Index .. Quotes.Last_Index loop
SUIO.Put_Line (Item => i'Img & " " & Quotes.Element (i));
end loop;
以下是得到的輸出
0 I have an ego the size of a small planet. 1 My name is Linus Torvalds and I am your god. 2 Do you pine for the days when men were men and wrote their own device drivers? 3 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 4 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 5 An infinite number of monkeys typing into GNU emacs would never make a good program. 6 Is "I hope you all die a painful death" too strong? 7 Most days I wake up thinking I'm the luckiest bastard alive. 8 I'm always right. This time I'm just even more right than usual. 9 And what's the internet without the rick-roll? 0 I have an ego the size of a small planet. 1 My name is Linus Torvalds and I am your god. 2 I have an ego the size of a small planet. 3 My name is Linus Torvalds and I am your god. 4 Do you pine for the days when men were men and wrote their own device drivers? 5 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 6 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 7 An infinite number of monkeys typing into GNU emacs would never make a good program. 8 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 9 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 10 An infinite number of monkeys typing into GNU emacs would never make a good program. 11 Is "I hope you all die a painful death" too strong? 12 Most days I wake up thinking I'm the luckiest bastard alive. 13 I'm always right. This time I'm just even more right than usual. 14 15 16 17 And what's the internet without the rick-roll?
首先,我們注意到在多次呼叫 Insert_Space 後,向量長度為 18。這與三個 Count 引數完美匹配:2 + 3 + 3 = 8 加上原始長度 10。然後,我們注意到在某些地方 Insert_Space 插入了一些看起來是空元素(位置 14-16),而在其他地方,則插入了已存在元素的副本(位置 0-1 和 8-9)。這展示了我在開頭提到的“不可預測的值”行為。
因此,當您使用 Insert_Space 時,不要將其視為一個完全按照其名稱含義執行的程式。將其視為一個僅擴充套件向量在指定索引處的長度的程式。換句話說:Insert_Space 主要用於在 Before 位置快速擴充套件向量,包含 Count 個元素,並且您不應該在將這些元素填充一些有效資料之前依賴它們的實際含義。因此,我們在最終迴圈中進行的訪問並非明智之舉。正如參考手冊引文中所述,我們可能會由於呼叫 Element 而遇到異常。
我們可以透過使用 Replace_Element 程式用一些真實資料填充空元素來避免此“問題”。
-- Output all the quotes prior to any Insert_Space calls
for i in Quotes.First_Index .. Quotes.Last_Index loop
SUIO.Put_Line (Item => i'Img & " " & Quotes.Element (i));
end loop;
IO.New_Line (2);
-- Insert two "empty" elements at the beginning of the vector
Quotes.Insert_Space (Before => 0,
Count => 2);
-- We now insert some actual data in the two new elements
Quotes.Replace_Element (Index => 0,
New_Item => To_Unbounded_String ("New index 0"));
Quotes.Replace_Element (Index => 1,
New_Item => To_Unbounded_String ("New index 1"));
-- Insert 3 "empty" elements before index 5 in the vector
Quotes.Insert_Space (Before => 5,
Count => 3);
-- We now insert some actual data in the three new elements
Quotes.Replace_Element (Index => 5,
New_Item => To_Unbounded_String ("New index 5"));
Quotes.Replace_Element (Index => 6,
New_Item => To_Unbounded_String ("New index 6"));
Quotes.Replace_Element (Index => 7,
New_Item => To_Unbounded_String ("New index 7"));
-- Insert 3 "empty" elements before the last element, using a cursor.
Q_Cursor := Quotes.To_Cursor (Index => Quotes.Last_Index);
Quotes.Insert_Space (Before => Q_Cursor,
Position => Q_Cursor,
Count => 3);
-- We now insert some actual data in the last three new elements
Quotes.Replace_Element (Index => 14,
New_Item => To_Unbounded_String ("New index 14"));
Quotes.Replace_Element (Index => 15,
New_Item => To_Unbounded_String ("New index 15"));
Quotes.Replace_Element (Index => 16,
New_Item => To_Unbounded_String ("New index 16"));
for i in Quotes.First_Index .. Quotes.Last_Index loop
SUIO.Put_Line (Item => i'Img & " " & Quotes.Element (i));
end loop;
輸出如下:
0 I have an ego the size of a small planet. 1 My name is Linus Torvalds and I am your god. 2 Do you pine for the days when men were men and wrote their own device drivers? 3 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 4 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 5 An infinite number of monkeys typing into GNU emacs would never make a good program. 6 Is "I hope you all die a painful death" too strong? 7 Most days I wake up thinking I'm the luckiest bastard alive. 8 I'm always right. This time I'm just even more right than usual. 9 And what's the internet without the rick-roll? 0 New index 0 1 New index 1 2 I have an ego the size of a small planet. 3 My name is Linus Torvalds and I am your god. 4 Do you pine for the days when men were men and wrote their own device drivers? 5 New index 5 6 New index 6 7 New index 7 8 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 9 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 10 An infinite number of monkeys typing into GNU emacs would never make a good program. 11 Is "I hope you all die a painful death" too strong? 12 Most days I wake up thinking I'm the luckiest bastard alive. 13 I'm always right. This time I'm just even more right than usual. 14 New index 14 15 New index 15 16 New index 16 17 And what's the internet without the rick-roll?
有了這些 Replace_Element 呼叫,我們可以在最終迴圈中使用 Element 函式安全地讀取整個向量,而無需擔心會引發任何異常。
與向向量新增資料一樣重要的是能夠讀取資料,所以讓我們來看看一些可用於執行此操作的方法。
我們在回顧之前過程和函式時已經遇到了 Element 函式,因此它做什麼以及它是如何工作的現在應該很清楚了。然而,這不會阻止我們像檢查 Vectors 包的其他部分一樣檢查它。以下是兩個 Element 函式的規範。
function Element
(Container : Vector;
Index : Index_Type) return Element_Type;
function Element (Position : Cursor) return Element_Type;
我們有一個 Element 函式,我們使用索引訪問元素,還有一個函式,我們使用遊標訪問元素。兩者都做同樣的事情:返回向量中給定位置的 Element_Type。以下是一個使用索引的示例。
-- Output first index 0 and then index 7
SUIO.Put_Line (Item => Quotes.Element (Index => 0));
SUIO.Put_Line (Item => Quotes.Element (Index => 7));
並且它的輸出是
I have an ego the size of a small planet. Most days I wake up thinking I'm the luckiest bastard alive.
遊標示例需要對 Quotes 程式的宣告部分進行新增。
Q_Cursor : Cursor;
之後,我們將此新增到 Quotes 程式的主體中。
-- Point the cursor at the first index of the vector
Q_Cursor := Quotes.To_Cursor (Index => Quotes.First_Index);
SUIO.Put_Line (Item => Element (Position => Q_Cursor));
-- Point the cursor at index 7 of the vector
Q_Cursor := Quotes.To_Cursor (Index => 7);
SUIO.Put_Line (Item => Element (Position => Q_Cursor));
輸出如下:
I have an ego the size of a small planet. Most days I wake up thinking I'm the luckiest bastard alive.
Element 非常簡單。使用索引還是遊標是個人喜好問題。值得注意的是,Vectors 包中大多數與遊標相關的過程和函式在內部將遊標轉換為索引,然後使用索引過程/函式進行實際的繁重處理。因此,我通常避免使用遊標形式。我認為索引更易於使用和理解。使用遊標形式的主要好處是靈活性。如果您使用遊標,您的程式碼將在很少修改的情況下與其他基於序列的容器一起工作,例如 雙向連結串列。
以下是 Query_Element 的規範。
procedure Query_Element
(Container : Vector;
Index : Index_Type;
Process : not null access procedure (Element : Element_Type));
procedure Query_Element
(Position : Cursor;
Process : not null access procedure (Element : Element_Type));
Query_Element 與 Element 函式既相似又不同。Element 返回給定索引的 Element_Type,而 Query_Element 將 Element_Type 傳遞給 Process 引數。此引數接受一個處理 Element_Type 的過程。為了展示它是如何工作的,我們首先需要在 Quotes 程式的規範中新增一個新過程。
-- This is the "callback" procedure
procedure Add_42 (Element : Unbounded_String) is
begin
SUIO.Put_Line (Item => Element & To_Unbounded_String (" 42"));
end Add_42;
我們在這個過程中所做的只是輸出元素並將 42 連線到輸出。
為了使用遊標,我們必須首先宣告它。
Q_Cursor : Cursor; -- Declare the cursor.
接下來這部分程式碼應該新增到 Quotes 程式的主體中。我們將 Query_Element 的索引版本和遊標版本都包含在同一個示例中。
-- Output the first index
Quotes.Query_Element (Index => 0,
Process => Add_42'Access);
-- Point the cursor at index 7 of the vector and output the element
Q_Cursor := Quotes.To_Cursor (Index => 7);
Query_Element (Position => Q_Cursor, Process => Add_42'Access);
注意 'Access 屬性。它返回一個指向 Add_42 子程式的指標,對於 Process 引數來說,它絕對是必需的。僅僅寫 Ada_42 是不夠的,正如您從規範中看到的,規範中說 not null access procedure。
此程式的輸出是
I have an ego the size of a small planet. 42 Most days I wake up thinking I'm the luckiest bastard alive. 42
使用 Element 還是 Query_Element 是效能與混亂之間的權衡。Query_Element 比 Element 速度更快,但是使用回撥過程的混亂程度可能不值得原始碼變得混亂,除非效能至關重要。元素越大,使用 Element 時效能下降就越嚴重。
更改向量中的資料是一項常見任務。以下兩種方法可以實現這一點。
如果您需要更新向量中的元素,Update_Element 過程將非常有用。它們的工作方式與 Query_Element 非常相似,即您必須建立一個回撥過程來處理您試圖對向量中的元素執行的操作。以下是 Update_Element 的規範。
procedure Update_Element
(Container : in out Vector;
Index : Index_Type;
Process : not null access procedure (Element : in out Element_Type));
procedure Update_Element
(Container : in out Vector;
Position : Cursor;
Process : not null access procedure (Element : in out Element_Type));
注意這兩個過程看起來幾乎完全像 Query_Element 過程。最關鍵的區別在於 Process 引數,現在引數模式為 in out,而 Query_Element 過程的引數模式為 in。由於它們如此相似,我們將從 Query_Element 示例 中宣告相同的遊標和回撥過程。我們只會更改引數模式。
procedure Add_42 (Element : in out Unbounded_String) is
begin
Element := Element & To_Unbounded_String (" 42");
end Add_42;
Q_Cursor : Cursor;
與 Query_Element 一樣,我將在同一個列表中顯示索引和遊標示例。
-- Update the first index
Quotes.Update_Element (Index => 0,
Process => Add_42'Access);
SUIO.Put_Line (Item => Quotes.Element (Index => 0));
-- Point the cursor at the index 7 of the vector
Q_Cursor := Quotes.To_Cursor (Index => 7);
Quotes.Update_Element (Position => Q_Cursor,
Process => Add_42'Access);
SUIO.Put_Line (Item => Quotes.Element (Index => 7));
此程式的輸出是
I have an ego the size of a small planet. 42 Most days I wake up thinking I'm the luckiest bastard alive. 42
Update_Element 並不是更改向量中元素內容的唯一方法。還有其他方法可以將“42”連線到我們的 Linus 引用,其中一種方法是 Replace_Element,我們將在後面討論它。
之前討論的 Update_Element 過程在原地更新元素,而 Replace_Element 將一個新的 Element_Type 賦值給給定的索引或位置,而不管之前的內容是什麼。以下是 Replace_Element 的規範。
procedure Replace_Element
(Container : in out Vector;
Index : Index_Type;
New_Item : Element_Type);
procedure Replace_Element
(Container : in out Vector;
Position : Cursor;
New_Item : Element_Type);
使用 Replace_Element 與使用 Insert 過程非常類似,除了您現在處理的索引已經包含一個值,而不是建立一個新的索引。保留 Update_Element 中的 Q_Cursor 宣告,並將以下內容新增到 Quotes 程式的主體中。
-- Replace the first index
SUIO.Put_Line (Item => Quotes.Element (Index => 0));
Quotes.Replace_Element (Index => 0,
New_Item => To_Unbounded_String ("New index 0"));
SUIO.Put_Line (Item => Quotes.Element (Index => 0));
-- Replace the index 7 using a cursor
SUIO.Put_Line (Item => Quotes.Element (Index => 7));
Q_Cursor := Quotes.To_Cursor (Index => 7);
Quotes.Replace_Element (Position => Q_Cursor,
New_Item => To_Unbounded_String ("New index 7"));
SUIO.Put_Line (Item => Quotes.Element (Index => 7));
此程式的輸出是
I have an ego the size of a small planet. New index 0 Most days I wake up thinking I'm the luckiest bastard alive. New index 7
何時使用 Replace_Element,何時使用 Update_Element 很大程度上取決於您想要完成的任務。請記住,Update_Element 在原地更新給定索引上的元素,而 Replace_Element 則完全替換給定索引上的元素,並且與 Element 和 Query_Element 一樣,Update_Element 比 Replace_Element 速度更快,但是它會使用回撥過程使原始碼變得混亂。因此,與許多其他事情一樣,這是乾淨程式碼與效能之間的權衡,所以一定要測試並選擇最適合專案的方案。
當然,可以從向量中刪除元素,並完全清除向量。我敢打賭,細心的讀者已經猜到了這些過程的名稱。然而,也有一些意外。
Vectors.Clear
[edit | edit source]以下是 Clear 的規範
procedure Clear (Container : in out Vector);
Clear 從向量中刪除所有元素,但不改變向量的容量。因此,如果您想重置一個向量,同時保留向量的容量,那麼 Clear 是您的最佳選擇。嘗試將其新增到 Quotes 程式的主體中
IO.Put_Line (Item => "Capacity before clear:" & Quotes.Capacity'Img);
IO.Put_Line (Item => "No. of quotes before clear:" & Quotes.Length'Img);
Quotes.Clear;
IO.Put_Line (Item => "Capacity after clear:" & Quotes.Capacity'Img);
IO.Put_Line (Item => "No. of quotes after clear:" & Quotes.Length'Img);
輸出如下:
Capacity before clear: 16 No. of quotes before clear: 10 Capacity after clear: 16 No. of quotes after clear: 0
請注意,您的容量數字可能與上面不同。需要注意的是,容量在呼叫 Clear 之前和之後是相同的。改變的是向量內容的長度值。
因此,不幸的是,您不能相信 Clear 能夠真正地從向量中刪除資料。這意味著,如果您調整現在“空”的向量的大小,您可能會再次找到您的資料。要檢視它是如何工作的,請將其新增到程式中
Quotes.Set_Length (Length => 10);
SUIO.Put_Line (Item => Quotes.First_Element);
輸出並非預期那樣,當然也不是想要的
Capacity before clear: 16 No. of quotes before clear: 10 Capacity after clear: 16 No. of quotes after clear: 0 I have an ego the size of a small planet.
Vectors.Delete
[edit | edit source]以下是 Delete 的規範
procedure Delete
(Container : in out Vector;
Index : Extended_Index;
Count : Count_Type := 1);
procedure Delete
(Container : in out Vector;
Position : in out Cursor;
Count : Count_Type := 1);
這兩個過程從向量中刪除元素。如您所見,有一個使用索引的版本和一個使用遊標的版本。我將在同一個示例中展示這兩個版本,因為它們在使用上非常相似。
Count 引數告訴 Delete 從向量中刪除多少個元素。如果元素數量少於 Count,則所有元素都會從向量中刪除。如果您嘗試刪除一個不存在的索引,則會引發 CONSTRAINT_ERROR 異常。
我們需要一個遊標來進行 Delete 示例,所以將其新增到 Quotes 程式的宣告部分
Q_Cursor : Cursor;
以下是 Delete 示例的主體程式碼
-- Delete quote no. 2 and 3
Quotes.Delete (Index => 1,
Count => 2);
-- Delete quote no. 5, 6 and 7
Q_Cursor := Quotes.To_Cursor (Index => 4);
Quotes.Delete (Position => Q_Cursor,
Count => 3);
for i in Quotes.First_Index .. Quotes.Last_Index loop
SUIO.Put_Line (Item => i'Img & " " & Quotes.Element (i));
end loop;
這是上面程式碼生成的輸出
0 I have an ego the size of a small planet. 1 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 2 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 3 An infinite number of monkeys typing into GNU emacs would never make a good program. 4 And what's the internet without the rick-roll?
如果 Count 引數為 0,則不做任何操作。
Vectors.Delete_First
[edit | edit source]以下是 Delete_First 的規範
procedure Delete_First
(Container : in out Vector;
Count : Count_Type := 1);
Delete_First 過程的作用應該很明顯。它刪除向量中的前 Count 個元素。要檢視示例,請將其新增到 Quotes 程式的主體中:
IO.Put_Line (Item => "No. of quotes:" & Quotes.Length'Img);
SUIO.Put_Line (Item => Quotes.First_Element);
Quotes.Delete_First;
IO.Put_Line (Item => "No. of quotes:" & Quotes.Length'Img);
SUIO.Put_Line (Item => Quotes.First_Element);
Quotes.Delete_First (Count => 5);
IO.Put_Line (Item => "No. of quotes:" & Quotes.Length'Img);
SUIO.Put_Line (Item => Quotes.First_Element);
上面程式的輸出是
No. of quotes: 10 I have an ego the size of a small planet. No. of quotes: 9 My name is Linus Torvalds and I am your god. No. of quotes: 4 Is "I hope you all die a painful death" too strong?
Delete_First 完全刪除了向量中的元素,它不僅刪除內容,還留下了空元素。如果您嘗試刪除比向量包含的元素更多的元素,則在整個向量被刪除後會引發 CONSTRAINT_ERROR。如果 Count 為 0,則 Delete_First 不執行任何操作。
Vectors.Delete_Last
[edit | edit source]以下是 Delete_Last 的規範
procedure Delete_Last
(Container : in out Vector;
Count : Count_Type := 1);
細心的讀者會注意到,這個過程與上面提到的 Delete_First 過程非常相似,因為它們非常相似,所以以下內容幾乎是 Delete_First 文字的逐字複製。
Delete_Last 過程會刪除向量中的最後 Count 個元素。要檢視示例,請將其新增到 Quotes 程式的主體中:
IO.Put_Line (Item => "No. of quotes:" & Quotes.Length'Img);
SUIO.Put_Line (Item => Quotes.Last_Element);
Quotes.Delete_Last;
IO.Put_Line (Item => "No. of quotes:" & Quotes.Length'Img);
SUIO.Put_Line (Item => Quotes.Last_Element);
Quotes.Delete_Last (Count => 5);
IO.Put_Line (Item => "No. of quotes:" & Quotes.Length'Img);
SUIO.Put_Line (Item => Quotes.Last_Element);
上面程式的輸出是
No. of quotes: 10 And what's the internet without the rick-roll? No. of quotes: 9 I'm always right. This time I'm just even more right than usual. No. of quotes: 4 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program.
Delete_Last 已完全刪除了向量中的元素。它也不會僅僅刪除內容,而留下一個或多個空元素,Count 的約束與 Delete_First 相同。
移動、交換和反轉
[edit | edit source]有時需要將整個向量移動到新的向量,交換向量中的值,或者可能以反向順序重新排列向量。以下方法將有助於實現這一點。
Vectors.Move
[edit | edit source]以下是 Move 的規範
procedure Move (Target : in out Vector; source : in out Vector);
可以使用 Move 過程將資料從一個向量移動到另一個向量。將其新增到 Quotes 程式的宣告部分
My_Quotes : Vector;
以及將其新增到主體中
IO.Put_Line (Item => "Quotes length:" & Quotes.Length'Img);
IO.Put_Line (Item => "My_Quotes length:" & My_Quotes.Length'Img);
My_Quotes.Move (source => Quotes);
IO.Put_Line (Item => "Quotes new length:" & Quotes.Length'Img);
IO.Put_Line (Item => "My_Quotes new length:" & My_Quotes.Length'Img);
輸出是
Quotes length: 10 My_Quotes length: 0 Quotes new length: 0 My_Quotes new length: 10
Vectors.Reverse_Elements
[edit | edit source]以下是 Reverse_Elements 的規範
procedure Reverse_Elements (Container : in out Vector);
Reverse_Elements 過程是另一個執行其名稱所指示操作的過程:它以反向順序重新排列向量中的元素。讓我們試一試
SUIO.Put_Line (Item => Quotes.First_Element);
Quotes.Reverse_Elements;
SUIO.Put_Line (Item => Quotes.First_Element);
輸出如下:
I have an ego the size of a small planet. And what's the internet without the rick-roll?
關於 Reverse_Elements 的內容基本上就是這些了。
Vectors.Swap
[edit | edit source]以下是 Swap 的規範
procedure Swap (Container : in out Vector; I, J : Index_Type);
procedure Swap (Container : in out Vector; I, J : Cursor);
兩個 Swap 過程將位置 I 和 J 的值相互交換,這意味著位置 I 的值被移動到位置 J,反之亦然。對於 Index_Type 過程和 Cursor 過程來說,功能相同,因此我將在同一個示例中展示這兩個過程。首先,我們必須將兩個新變數新增到 Quotes 程式的宣告部分
A_Cursor : Cursor;
B_Cursor : Cursor;
然後將其新增到主體中
SUIO.Put_Line (Item => Quotes.First_Element);
Quotes.Swap (I => 0,
J => 9);
SUIO.Put_Line (Item => Quotes.First_Element);
A_Cursor := Quotes.To_Cursor (Index => 0);
B_Cursor := Quotes.To_Cursor (Index => 9);
SUIO.Put_Line (Item => Quotes.First_Element);
Quotes.Swap (I => A_Cursor,
J => B_Cursor);
SUIO.Put_Line (Item => Quotes.First_Element);
此程式的輸出是
I have an ego the size of a small planet. And what's the internet without the rick-roll? And what's the internet without the rick-roll? I have an ego the size of a small planet.
請注意,如果要交換的索引之一超出範圍,則會引發 Constraint_Error 異常。
使用 Swap,非常容易地將 氣泡排序 新增到 Quotes 程式中。請注意,我這裡僅僅使用氣泡排序作為示例,您不應該使用這種排序方法來排序您的資料,因為它非常慢且效率低下。
可以使用以下方法獲取有關給定向量的相當多的資訊。
Capacity 返回向量的當前容量。重要的是要理解,容量與向量的尺寸不同;相反,您應該將其視為向量在必須為向量分配新資料結構之前可以增長的尺寸。
以下是 Capacity 的規範
function Capacity (Container : Vector) return Count_Type;
讓我們看看它是如何工作的。將此新增到 Quotes 程式的正文中
IO.Put_Line (Item => "Capacity:" & Quotes.Capacity'Img);
Quotes.Clear;
IO.Put_Line (Item => "Capacity:" & Quotes.Capacity'Img);
輸出如下:
Capacity: 16 Capacity: 16
Quotes 向量的容量為 16,即使我們知道它只有 10 個引號。即使我們清空 Quotes 向量,容量仍然保持在 16。讓我們擴充套件一下 Quotes 程式,看看向量包是如何處理容量的。我將在此處釋出完整的原始碼
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Containers.Vectors; use Ada.Containers;
procedure Quotes is
package IO renames Ada.Text_IO;
package SUIO renames Ada.Text_IO.Unbounded_IO;
package Quote_Container is new Vectors (Natural, Unbounded_String);
use Quote_Container;
Quotes : Vector;
Input : IO.File_Type;
begin
IO.Put_Line (Item => "Capacity (pre loop):" & Quotes.Capacity'Img);
IO.Put_Line (Item => "Length (pre loop):" & Quotes.Length'Img);
IO.Open (File => Input,
Mode => IO.In_File,
Name => "quotes.txt");
while not IO.End_Of_File (File => Input) loop
Quotes.Append (New_Item => SUIO.Get_Line (File => Input));
IO.Put_Line (Item => "Capacity:" & Quotes.Capacity'Img);
IO.Put_Line (Item => "Length:" & Quotes.Length'Img);
end loop;
IO.Close (Input);
IO.Put_Line (Item => "Capacity (post loop):" & Quotes.Capacity'Img);
IO.Put_Line (Item => "Length (post loop):" & Quotes.Length'Img);
end Quotes;
輸出如下:
Capacity (pre loop): 0 Length (pre loop): 0 Capacity: 1 Length: 1 Capacity: 2 Length: 2 Capacity: 4 Length: 3 Capacity: 4 Length: 4 Capacity: 8 Length: 5 Capacity: 8 Length: 6 Capacity: 8 Length: 7 Capacity: 8 Length: 8 Capacity: 16 Length: 9 Capacity: 16 Length: 10 Capacity (post loop): 16 Length (post loop): 10
注意長度和容量並不一定匹配。向量的容量將始終與長度相同或大於長度。當我們將容量調整留給系統時,容量似乎以 2 的冪增長,而長度是實際新增資料的線性計數。
Is_Empty 的規範如下所示
function Is_Empty (Container : Vector) return Boolean;
使用 Is_Empty 函式,我們可以測試向量是否為空。將此新增到 Quotes 的正文中,以檢視它是如何工作的
if Quotes.Is_Empty then
IO.Put_Line (Item => "Empty!");
else
IO.Put_Line (Item => "Not empty!");
end if;
Quotes.Clear;
if Quotes.Is_Empty then
IO.Put_Line (Item => "Empty!");
else
IO.Put_Line (Item => "Not empty!");
end if;
輸出如下:
Not empty! Empty!
請注意,在本例中,空是指沒有元素的向量,而不是包含一堆空元素的向量。Is_Empty 函式檢查的是向量的長度。如果長度大於 0,則 Is_Empty 返回 **FALSE**,如果長度等於 0,則 Is_Empty 返回 **TRUE**。
以下是Length 的規範:
function Length (Container : Vector) return Count_Type;
我們已經在前面的示例中遇到了 Length 函式,但是如果您沒有注意它們,以下是對 Length 的簡要說明:它返回向量的長度(即元素的數量)。我敢打賭這是一個驚喜!
讓我們看一個例子。將此新增到 Quotes 程式的正文中
IO.Put_Line (Item => "Length of vector:" & Quotes.Length'Img);
Quotes.Clear;
IO.Put_Line (Item => "Length of vector:" & Quotes.Length'Img);
輸出如下:
10 0
最後讓我們看看 Length。
遍歷向量可以使用簡單的迴圈來完成,但為什麼不使用內建方法呢?這些方法不如定製迴圈靈活,但如果我們只需要一個簡單的“陣列遍歷”,那麼這些方法就是最好的選擇。
使用索引或遊標遍歷向量非常容易,但還有一個更簡單的方法:Iterate 過程。
以下是 Iterate 的規範
procedure Iterate
(Container : Vector;
Process : not null access procedure (Position : Cursor));
Iterate 使您能夠將 Process 過程應用於向量中的每個元素。與 Query_Element 和 Update_Element 過程一樣,我們必須建立一個過程來處理各個元素。在這個示例中,我們將使用一個小的過程來反轉每個引號,然後將生成的亂碼輸出到螢幕上。將此新增到 Quotes 程式的宣告部分
procedure Reverse_Quote (A_Cursor : Cursor) is
Org_Quote : constant Unbounded_String := Element (Position => A_Cursor);
New_Backwards_Quote : Unbounded_String;
begin
for i in reverse 1 .. Length (Source => Org_Quote) loop
Append (Source => New_Backwards_Quote,
New_Item => Element (Source => Org_Quote,
Index => i));
end loop;
SUIO.Put_Line (Item => New_Backwards_Quote);
end Reverse_Quote;
接下來將此新增到正文中
Quotes.Iterate (Process => Reverse_Quote'Access);
此程式的輸出是
.tenalp llams a fo ezis eht oge na evah I .dog ruoy ma I dna sdlavroT suniL si eman yM ?srevird ecived nwo rieht etorw dna nem erew nem nehw syad eht rof enip uoy oD .margorp ruoy xif dluohs dna ,yawyna dewercs er'uoy ,noitatnedni fo slevel 3 naht erom deen uoy fI .won morf skeew 2 did uoy tahw dnatsrednu ot ekil d'uoy ebyam tub ,tnaillirb er'uoy wonk uoY .margorp doog a ekam reven dluow scame UNG otni gnipyt syeknom fo rebmun etinifni nA ?gnorts oot "htaed lufniap a eid lla uoy epoh I" sI .evila dratsab tseikcul eht m'I gnikniht pu ekaw I syad tsoM .lausu naht thgir erom neve tsuj m'I emit sihT .thgir syawla m'I ?llor-kcir eht tuohtiw tenretni eht s'tahw dnA
非常優雅。將 Iterate 用於向量中元素的簡單操作正是它被設計出來的目的。對於非常複雜的操作,它可能不是最佳解決方案,因為您無法將任何引數傳遞給 Process 過程,這自然限制了它的一些用法。
以下是 Reverse_Iterate 的規範
procedure Reverse_Iterate
(Container : Vector;
Process : not null access procedure (Position : Cursor));
Iterate 從第一個元素遍歷到最後一個元素,而 Reverse_Iterate 則相反。除了這個小區別之外,這兩個過程完全相同,因此我將展示的關於如何使用 Reverse_Iterate 的示例幾乎與 Iterate 的示例相同。
首先,我們將 Reverse_Quote 過程新增到 Quotes 程式的宣告部分
procedure Reverse_Quote (A_Cursor : Cursor) is
Org_Quote : constant Unbounded_String := Element (Position => A_Cursor);
Backwards : Unbounded_String;
Index : constant Natural := To_Index (Position => A_Cursor);
begin
for i in reverse 1 .. Length (Source => Org_Quote) loop
Append (Source => Backwards,
New_Item => Element (Source => Org_Quote,
Index => i));
end loop;
IO.Put (Item => Index'Img & " ");
SUIO.Put (Item => Backwards);
IO.New_Line;
end Reverse_Quote;
/code>
And the we add this line to the body of the program:
<source lang="ada">
Quotes.Reverse_Iterate (Process => Reverse_Quote'Access);
輸出結果如預期
9 ?llor-kcir eht tuohtiw tenretni eht s'tahw dnA 8 .lausu naht thgir erom neve tsuj m'I emit sihT .thgir syawla m'I 7 .evila dratsab tseikcul eht m'I gnikniht pu ekaw I syad tsoM 6 ?gnorts oot "htaed lufniap a eid lla uoy epoh I" sI 5 .margorp doog a ekam reven dluow scame UNG otni gnipyt syeknom fo rebmun etinifni nA 4 .won morf skeew 2 did uoy tahw dnatsrednu ot ekil d'uoy ebyam tub ,tnaillirb er'uoy wonk uoY 3 .margorp ruoy xif dluohs dna ,yawyna dewercs er'uoy ,noitatnedni fo slevel 3 naht erom deen uoy fI 2 ?srevird ecived nwo rieht etorw dna nem erew nem nehw syad eht rof enip uoy oD 1 .dog ruoy ma I dna sdlavroT suniL si eman yM 0 .tenalp llams a fo ezis eht oge na evah I
反轉方向的反轉引號。與 Iterate 一樣,您無法將任何引數與 Process 過程一起使用。
無論您是使用遊標還是索引,有時您都需要識別向量的開頭和/或結尾。為此特定工作,我們有 4 種方法可用。讓我們來看看它們。
以下是 First_Index 的規範
function First_Index (Container : Vector) return Index_Type;
使用 First_Index 的好處(除了清晰度之外)並不容易顯現,除非我們建立幾個具有不同 Index_Type 引數的向量。我將在此處顯示完整的原始碼,因為所需的更改非常多
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Containers.Vectors; use Ada.Containers;
procedure Quotes is
package IO renames Ada.Text_IO;
package SUIO renames Ada.Text_IO.Unbounded_IO;
subtype Foo_Range is Natural range 42 .. 52;
subtype Bar_Range is Integer range -100 .. 100;
package Quote_Container is new Vectors (Natural, Unbounded_String);
package Foo_Container is new Vectors (Foo_Range, Unbounded_String);
package Bar_Container is new Vectors (Bar_Range, Unbounded_String);
Quotes : Quote_Container.Vector;
Foo : Foo_Container.Vector;
Bar : Bar_Container.Vector;
Input : IO.File_Type;
Line : Unbounded_String;
begin
IO.Open (File => Input,
Mode => IO.In_File,
Name => "quotes.txt");
while not IO.End_Of_File (File => Input) loop
Line := SUIO.Get_Line (File => Input);
Quotes.Append (New_Item => Line);
Foo.Append (New_Item => Line);
Bar.Append (New_Item => Line);
end loop;
IO.Close (Input);
IO.Put_Line (Item => Quotes.First_Index'Img);
for i in Quotes.First_Index .. Quotes.Last_Index loop
IO.Put (Item => i'Img & " ");
SUIO.Put (Item => Quotes.Element (Index => i));
IO.New_Line;
end loop;
IO.New_Line;
IO.Put_Line (Item => Foo.First_Index'Img);
for i in Foo.First_Index .. Foo.Last_Index loop
IO.Put (Item => i'Img & " ");
SUIO.Put (Item => Foo.Element (Index => i));
IO.New_Line;
end loop;
IO.New_Line;
IO.Put_Line (Item => Bar.First_Index'Img);
for i in Bar.First_Index .. Bar.Last_Index loop
IO.Put (Item => i'Img & " ");
SUIO.Put (Item => Bar.Element (Index => i));
IO.New_Line;
end loop;
end Quotes;
此程式的輸出是
0 0 I have an ego the size of a small planet. 1 My name is Linus Torvalds and I am your god. 2 Do you pine for the days when men were men and wrote their own device drivers? 3 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 4 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 5 An infinite number of monkeys typing into GNU emacs would never make a good program. 6 Is "I hope you all die a painful death" too strong? 7 Most days I wake up thinking I'm the luckiest bastard alive. 8 I'm always right. This time I'm just even more right than usual. 9 And what's the internet without the rick-roll? 42 42 I have an ego the size of a small planet. 43 My name is Linus Torvalds and I am your god. 44 Do you pine for the days when men were men and wrote their own device drivers? 45 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. 46 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. 47 An infinite number of monkeys typing into GNU emacs would never make a good program. 48 Is "I hope you all die a painful death" too strong? 49 Most days I wake up thinking I'm the luckiest bastard alive. 50 I'm always right. This time I'm just even more right than usual. 51 And what's the internet without the rick-roll? -100 -100 I have an ego the size of a small planet. -99 My name is Linus Torvalds and I am your god. -98 Do you pine for the days when men were men and wrote their own device drivers? -97 If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. -96 You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. -95 An infinite number of monkeys typing into GNU emacs would never make a good program. -94 Is "I hope you all die a painful death" too strong? -93 Most days I wake up thinking I'm the luckiest bastard alive. -92 I'm always right. This time I'm just even more right than usual. -91 And what's the internet without the rick-roll?
注意所有引號前面的數字是如何根據用作向量 Index_Type 的子型別來設定的。您應該始終使用 First_Index 函式而不是實際的數值,主要是因為它使您能夠以後更改 Index_Type,而無需搜尋所有程式碼以查詢對硬編碼數值的引用。使用 First_Index 更加安全。
以下是 Last_Index 的規範
function Last_Index (Container : Vector) return Extended_Index;
Last_Index 有點奇怪,因為它返回一個 Extended_Index 型別的
讓我們看看它是如何工作的。將此新增到程式的宣告部分
Empty : Vector;
將此新增到程式的主體
IO.Put_Line (Item => Quotes.Last_Index'Img);
IO.Put_Line (Item => Empty.Last_Index'Img);
輸出如下:
9 -1
如您所見,Last_Index 函式對於 Empty 向量返回 -1。我們用 Natural 作為 Index_Type 例項化了此向量,如您所知,Natural 型別具有 0 .. Integer'Last 的範圍,而 0 減 1 等於 -1。
為了進一步證明這一點,嘗試將這兩個 if 塊新增到主體中
if Empty.Last_Index = No_Index then
IO.Put_Line ("No_Index");
end if;
if Empty.Last_Index = Empty.First_Index - 1 then
IO.Put_Line ("No_Index");
end if;
現在輸出是
9 -1 No_Index No_Index
在他的優秀著作《Ada 2005 程式設計》 (ISBN 0321340787) 中,John Barnes 這樣說
請注意,必須引入令人厭煩的子型別Extended_Index來處理結束值。
我不知道是否會稱 Extended_Index 為令人厭煩,但它是一個應該意識到並理解的怪事。
以下是 First 的規範
function First (Container : Vector) return Cursor;
First 函式返回一個指向向量中第一個元素的遊標。與 First_Index 一樣,使用 First 的主要原因是避免在程式中硬編碼 Index_Type 的第一個值。
將此新增到 Quotes 程式的宣告部分
Q_Cursor : Cursor;
以及將其新增到主體中
-- This is bad. If we change the Index_Type from Natural to Positive,
-- 0 would no longer be the first position in the vector.
Q_Cursor := Quotes.To_Cursor (Index => 0);
SUIO.Put_Line (Item => Element (Position => Q_Cursor));
-- This is good. We always get the first position, no matter what
-- Index_Type we're using.
Q_Cursor := Quotes.First;
SUIO.Put_Line (Item => Element (Position => Q_Cursor));
輸出是
I have an ego the size of a small planet. I have an ego the size of a small planet.
您應該始終使用 First_Index 或 First 來識別向量中的第一個位置。您**絕不**應該硬編碼第一個位置的數值,因為這種方法在您更改向量的 Index_Type 時有失敗的風險。
以下是 Last 的規範
function Last (Container : Vector) return Cursor;
Last 比它的兄弟 Last_Index 簡單得多,僅僅因為它不需要處理“令人厭煩”的 Extended_Index 型別。Last 函式只是返回一個指向向量中最後一個元素的遊標,或者在空向量的情況下,它返回 No_Element。讓我們看看它是如何工作的。
將此新增到 Quotes 程式的宣告部分
Q_Cursor : Cursor;
以及將其新增到主體中
Q_Cursor := Quotes.Last;
SUIO.Put_Line (Item => Element (Position => Q_Cursor));
Quotes.Clear;
Q_Cursor := Quotes.Last;
if Q_Cursor /= No_Element then
SUIO.Put_Line (Item => Element (Position => Q_Cursor));
else
IO.Put_Line (Item => "No Elements in vector");
end if;
此程式的輸出是
And what's the internet without the rick-roll? No Elements in vector
如您所見,當向量為空時,遊標的值將設定為 No_Element。如果您嘗試“讀取”No_Element 遊標,將引發 CONSTRAINT_ERROR。
我們已經瞭解瞭如何使用 Iterate、Reverse_Iterate 和使用索引的常規迴圈來迭代向量,因此我認為現在該學習如何使用遊標來完成它了。為此,我們提供了 Next 和 Previous 方法。
以下是 Next 的規範
function Next (Position : Cursor) return Cursor;
procedure Next (Position : in out Cursor);
根據您的口味和偏好,您可以在使用 Next 函式或 Next 過程之間進行選擇。它們都執行相同的操作:將遊標移動到向量中的下一個元素。我將在下面的示例中展示這兩個版本。
首先將此新增到程式的宣告部分
A_Cursor : Cursor;
以及將此新增到主體
A_Cursor := Quotes.First;
SUIO.Put_Line (Item => Element (Position => A_Cursor));
-- Move to the next element in the vector using the Next procedure.
Next (Position => A_Cursor);
SUIO.Put_Line (Item => Element (Position => A_Cursor));
-- Move to the next Element in the vector using the Next function.
A_Cursor := Next (Position => A_Cursor);
SUIO.Put_Line (Item => Element (Position => A_Cursor));
-- Move the cursor to the last position, and then call Next again.
-- Note how Next assigns the No_Element value to the cursor.
A_Cursor := Quotes.Last;
SUIO.Put_Line (Item => Element (Position => A_Cursor));
Next (Position => A_Cursor);
if A_Cursor = No_Element then
IO.Put_Line (Item => "No_Element");
end if;
輸出如下:
I have an ego the size of a small planet. My name is Linus Torvalds and I am your god. Do you pine for the days when men were men and wrote their own device drivers? No_Element
希望進一步解釋是不必要的。
以下是 Previous 的規範
function Previous (Position : Cursor) return Cursor;
procedure Previous (Position : in out Cursor);
在 Next 方法向前移動遊標的情況下,Previous 方法向後移動遊標。我相信這一點大家都清楚,因此,讓我們看看 Previous 在實際程式碼中是如何工作的。
將此新增到宣告部分
A_Cursor : Cursor;
以及將其新增到主體中
A_Cursor := Quotes.Last;
SUIO.Put_Line (Item => Element (Position => A_Cursor));
-- Move to the previous element in the vector using the Previous procedure.
Previous (Position => A_Cursor);
SUIO.Put_Line (Item => Element (Position => A_Cursor));
-- Move to the previous Element in the vector using the Previous function.
A_Cursor := Previous (Position => A_Cursor);
SUIO.Put_Line (Item => Element (Position => A_Cursor));
-- Move the cursor to the first position, and then call Previous again.
-- Note how Previous assigns the No_Element value to the cursor.
A_Cursor := Quotes.First;
SUIO.Put_Line (Item => Element (Position => A_Cursor));
Previous (Position => A_Cursor);
if A_Cursor = No_Element then
IO.Put_Line (Item => "No_Element");
end if;
輸出如下:
And what's the internet without the rick-roll? I'm always right. This time I'm just even more right than usual. Most days I wake up thinking I'm the luckiest bastard alive. I have an ego the size of a small planet. No_Element
這裡應該沒有驚喜。
找出特定元素是否存在於向量中,或者給定位置是否包含值,這是在使用向量時相當常見的任務,因此,我們提供了一些有價值的工具。
以下是 Find_Index 的規範
function Find_Index
(Container : Vector;
Item : Element_Type;
Index : Index_Type := Index_Type'First) return Extended_Index;
Find_Index 函式實際上只是圍繞一個簡單迴圈的包裝器,其中向量中的每個元素都與 Item 進行比較。它沒有任何“神奇”之處。它不會比自制迴圈快,但可以為您節省幾行程式碼。Find_Index 從 Index 開始向前搜尋向量。Index 預設為 Index_Type'First,在本例中為 0。
要了解它是如何工作的,我們首先必須在程式的宣告部分新增一些變數
Needle : Unbounded_String;
Pos : Extended_Index;
然後將此新增到主體
-- First search. This will fail because we have no FooBar quote.
Needle := To_Unbounded_String ("FooBar");
Pos := Quotes.Find_Index (Item => Needle);
if Pos = No_Index then
IO.Put_Line ("No_Index");
else
IO.Put_Line (Pos'Img);
end if;
-- Second search. This will succeed, finding the quote at index 7.
Needle := To_Unbounded_String
("Most days I wake up thinking I'm the luckiest bastard alive.");
Pos := Quotes.Find_Index (Item => Needle);
if Pos = No_Index then
IO.Put_Line ("No_Index");
else
IO.Put_Line (Pos'Img);
end if;
-- Third search. This will fail, because we start the search at index 8.
Needle := To_Unbounded_String
("Most days I wake up thinking I'm the luckiest bastard alive.");
Pos := Quotes.Find_Index (Item => Needle,
Index => 8);
if Pos = No_Index then
IO.Put_Line ("No_Index");
else
IO.Put_Line (Pos'Img);
end if;
最後輸出
No_Index 7 No_Index
本例中有三個有趣的地方
Pos的型別為Extended_Index。這是為了處理No_Index值。- 使用
Find_Index的Index引數可以讓我們從指定索引開始搜尋。 - 如果未找到匹配的元素,則返回
No_Index值。
除了這三件事之外,關於 Find_Index 真的沒什麼好說的了。
在這個示例中,您將找到上面的 Find_Index 示例,它被修改為使用 Find。這兩個函式在使用和功能方面非常相似,因此,進一步解釋完全是浪費空間。所以您只得到規範和示例。
以下是 Find 的規範
function Find
(Container : Vector;
Item : Element_Type;
Position : Cursor := No_Element) return Cursor;
Reverse_Find_Index 與 Find_Index 類似,區別在於它從 Index 開始反向搜尋,直到到達向量開頭或找到匹配項。在示例原始碼頁面上,您會發現 Find_Index 示例被修改以反映這一點。
Reverse_Find_Index 的規範與 Find_Index 驚人地相似
function Reverse_Find_Index
(Container : Vector;
Item : Element_Type;
Index : Index_Type := Index_Type'Last) return Extended_Index;
Reverse_Find 與 Find 類似,區別在於它從 Position 開始反向搜尋,直到到達向量開頭或找到匹配項。在示例原始碼頁面上,您會發現 Find 示例被修改以反映這一點。
Reverse_Find 的規範與 Find 驚人地相似
function Reverse_Find
(Container : Vector;
Item : Element_Type;
Position : Cursor := No_Element) return Cursor;
這是 Contains 的規範
function Contains
(Container : Vector;
Item : Element_Type) return Boolean;
使用 Contains,您基本上獲得了 Find_Index,但返回的是一個 Boolean 值,而不是 Extended_Index,實際上 Contains 函式只是 Find_Index 函式的薄包裝器。如果您正在處理非常大的向量,並且知道您要查詢的元素位於向量的末尾附近,從效能的角度來看,您可能最好使用自制的解決方案。我稍後會向您展示一個這樣的解決方案。
但首先讓我們嘗試一下 Contains 示例。將其新增到宣告中
Needle : Unbounded_String;
以及主體
Needle := To_Unbounded_String ("FooBar");
if Quotes.Contains (Item => Needle) then
IO.Put_Line (Item => "Found!");
else
IO.Put_Line (Item => "Not found!");
end if;
Needle := To_Unbounded_String
("Most days I wake up thinking I'm the luckiest bastard alive.");
if Quotes.Contains (Item => Needle) then
IO.Put_Line (Item => "Found!");
else
IO.Put_Line (Item => "Not found!");
end if;
最後輸出
Not found! Found!
如前所述,Contains 從頭到尾搜尋向量,但這可能不是最佳解決方案。有時您知道一段資料位於向量的末尾附近,如果它在那裡的話。在這種情況下,我們需要一個反向搜尋的 Contains 函式。這樣的函式很容易實現
. 由於兩個 Find 函式都提供了 Reverse_ 版本,我發現沒有提供 Reverse_Contains 函式很奇怪。幸運的是,如前面的原始碼所示,很容易自己新增它。
這是 Has_Element 的規範
function Has_Element (Position : Cursor) return Boolean;
Has_Element 使您能夠檢查遊標是否標識了一個元素,或者如 ARM 所說
如果 Position 指定了一個元素,則返回 True,否則返回 False。
因此 Has_Element 使您能夠檢查遊標是否仍然指向向量中的有效元素。請記住,遊標指定了容器和容器中的位置。這意味著遊標可以指向向量中不再存在的某個位置。讓我們看看它是如何使用的。將其新增到程式的宣告部分
A_Cursor : Cursor;
現在將其新增到主體中
-- First check. Cursor is not yet pointing at any specific index
if not Has_Element (Position => A_Cursor) then
IO.Put (Item => To_Index (Position => A_Cursor)'Img & " ");
IO.Put_Line (Item => "No Element!");
end if;
-- Point cursor at the last index
A_Cursor := Quotes.To_Cursor (Index => Quotes.Last_Index);
if Has_Element (Position => A_Cursor) then
IO.Put (Item => To_Index (Position => A_Cursor)'Img & " ");
IO.Put_Line (Item => "Element!");
end if;
-- Delete the first position. Now the cursor is pointing at a non-
-- existant position, represented by the numeric value -1
Quotes.Delete_First;
if not Has_Element (Position => A_Cursor) then
IO.Put (Item => To_Index (Position => A_Cursor)'Img & " ");
IO.Put_Line (Item => "No Element!");
end if;
-- Move the cursor on position backwards, from 9 to 8.
Previous (Position => A_Cursor);
-- Now check if the current position designates an element
if Has_Element (Position => A_Cursor) then
IO.Put (Item => To_Index (Position => A_Cursor)'Img & " ");
IO.Put_Line (Item => "Element!");
end if;
輸出如下:
-1 No Element! 9 Element! -1 No Element! 8 Element!
說實話,在使用向量時,我從來沒有使用過 Has_Element,但這可能是因為我很少在向量中使用遊標。使用Ada.Containers.Doubly_Linked_Lists,情況就不同了,因為導航雙向連結串列的唯一方法是使用遊標。
有時需要將索引轉換為遊標,反之亦然。這可以使用名為 To_Cursor 和 To_Index 的函式來完成。
這是 To_Cursor 的規範
function To_Cursor
(Container : Vector;
Index : Extended_Index) return Cursor;
我將向您展示上述函式是如何工作的,即使它應該非常明顯。首先將其新增到程式的宣告部分
A_Cursor : Cursor;
以及將其新增到主體中
-- Point the cursor to index 1
A_Cursor := Quotes.To_Cursor (Index => 1);
SUIO.Put_Line (Item => Element (Position => A_Cursor));
-- Point the cursor to index 5
A_Cursor := Quotes.To_Cursor (Index => 5);
SUIO.Put_Line (Item => Element (Position => A_Cursor));
-- Point the cursor to a non-existant index
A_Cursor := Quotes.To_Cursor (Index => 42);
if A_Cursor = No_Element then
IO.Put_Line (Item => "No_Element");
end if;
該程式產生的輸出結果為
My name is Linus Torvalds and I am your god. An infinite number of monkeys typing into GNU emacs would never make a good program. No_Element
從這個例子中,需要了解的重要一點是 No_Element 部分。如果給定的 Index 超出了範圍,則返回 No_Element。
這是 To_Index 的規範
function To_Index (Position : Cursor) return Extended_Index;
上面我們簡要了解了如何將 Index_Type 轉換為遊標;現在我們將執行相反的操作:從遊標轉換為 Index_Type。這可以使用 To_Index 函式來完成。
您可以保留 To_Cursor 示例的宣告部分,但將主體替換為以下內容
-- Point the cursor to the first index and output the index value
A_Cursor := Quotes.To_Cursor (Index => Quotes.First_Index);
IO.Put_Line (Item => To_Index (Position => A_Cursor)'Img);
-- Point the cursor to the 5th. index and output the index value
A_Cursor := Quotes.To_Cursor (Index => 5);
IO.Put_Line (Item => To_Index (Position => A_Cursor)'Img);
-- Point the cursor to a non-existant index, convert to an index and
-- check for No_Index
A_Cursor := Quotes.To_Cursor (Index => 42);
if To_Index (Position => A_Cursor) = No_Index then
IO.Put_Line (Item => "No_Index");
end if;
這並不是什麼難事,但值得注意的是,當對指向 No_Element 的遊標呼叫 To_Index 時,No_Element 將被“轉換為” No_Index。
向量包還包含用於對向量進行排序的工具,以泛型包 Generic_Sorting 的形式,因此無需使用自制的排序例程。排序以升序進行。Generic_Sorting 的規範如下所示
generic
with function "<" (Left, Right : Element_Type) return Boolean is <>;
package Generic_Sorting is
function Is_Sorted (Container : Vector) return Boolean;
procedure Sort (Container : in out Vector);
procedure Merge (Target : in out Vector; Source : in out Vector);
end Generic_Sorting;
為了使用這些過程,我們首先需要例項化 Generic_Sorting 泛型。這是以顯而易見的方式完成的
package A_Sorter is new Generic_Sorting;
只需將以上內容新增到 Quotes 程式的宣告部分,您就可以開始使用。在下文中,我們將瞭解如何使用泛型過程來對向量進行排序。
這是 Sort 的規範
procedure Sort (Container : in out Vector);
除了例項化 Generic_Sorting 泛型(參見 Vectors.Generic_Sorting)之外,我們無需在 Quotes 程式的宣告部分指定任何其他內容。相反,我們將直接跳轉到主體,在那裡我們將新增幾行以瞭解排序是如何工作的
A_Sorter.Sort (Container => Quotes);
for i in Quotes.First_Index .. Quotes.Last_Index loop
SUIO.Put_Line (Item => Quotes.Element (Index => i));
end loop;
程式的輸出結果為
An infinite number of monkeys typing into GNU emacs would never make a good program. And what's the internet without the rick-roll? Do you pine for the days when men were men and wrote their own device drivers? I have an ego the size of a small planet. I'm always right. This time I'm just even more right than usual. If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. Is "I hope you all die a painful death" too strong? Most days I wake up thinking I'm the luckiest bastard alive. My name is Linus Torvalds and I am your god. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now.
就這樣,您已經擁有了一個完美排序的 quotes.txt 檔案。比這更簡單的排序方式沒有了吧。
以下是 Is_Sorted 的規格
function Is_Sorted (Container : Vector) return Boolean;
使用這個方便的功能來判斷向量是否已排序。如果已排序,它將返回 Boolean TRUE,否則返回 Boolean FALSE。以下是它的工作原理
if not A_Sorter.Is_Sorted (Container => Quotes) then
IO.Put_Line ("We have not yet sorted the Quotes vector");
end if;
A_Sorter.Sort (Container => Quotes);
if A_Sorter.Is_Sorted (Container => Quotes) then
IO.Put_Line ("Now we have sorted the Quotes vector");
end if;
程式的輸出結果為
We have no yet sorted the Quotes vector Now we have sorted the Quotes vector
我相信以上結果對你來說並不意外。
但是,如果向量已經排序,然後新增一個新元素呢?Is_Sorted 還會返回 Boolean TRUE 嗎?將以下程式碼新增到上面的程式碼中,然後看看結果
Quotes.Append (New_Item => To_Unbounded_String ("New stuff!"));
if not A_Sorter.Is_Sorted (Container => Quotes) then
IO.Put_Line ("The vector is no longer sorted");
end if;
現在輸出看起來像這樣
We have no yet sorted the Quotes vector Now we have sorted the Quotes vector The vector is no longer sorted
正如預期的那樣,如果對向量進行了更改,Is_Sorted 函式將返回 Boolean FALSE,但這並不是透過某種內部“標誌”來實現的。不,FALSE 值是透過簡單地遍歷整個向量來發現的,同時檢查位置 i + 1 是否小於位置 i。如果是這種情況,則 Is_Sorted 返回 Boolean FALSE。
對於大型向量來說,這效率並不高。因此,當然,你應該只在絕對必要時使用 Is_Sorted。
以下是 Merge 的規格
procedure Merge (Target : in out Vector; Source : in out Vector);
Merge 很特別。它將 Target 與 Source 合併,並將 Source 設為空。特別之處在於它不會對結果向量進行排序,而這正是我們對 Merge 的預期,因為它是 Generic_Sorting 的一部分。如果你希望 Merge 的結果是一個排序的向量,那麼在呼叫 Merge 之前,你必須對 Target 和 Source 進行排序。
讓我們看看它是如何工作的。將此新增到 Quotes 程式的宣告部分
Foo_Bar : Vector;
package A_Sorter is new Generic_Sorting;
以及將其新增到主體中
Foo_Bar.Append (New_Item => To_Unbounded_String ("Foo"));
Foo_Bar.Append (New_Item => To_Unbounded_String ("Bar"));
A_Sorter.Sort (Container => Foo_Bar);
A_Sorter.Sort (Container => Quotes);
A_Sorter.Merge (Target => Foo_Bar,
Source => Quotes);
for i in Foo_Bar.First_Index .. Foo_Bar.Last_Index loop
SUIO.Put_Line (Item => Foo_Bar.Element (Index => i));
end loop;
IO.Put_Line (Item => "Length of Quotes:" & Quotes.Length'Img);
此程式的輸出是
An infinite number of monkeys typing into GNU emacs would never make a good program. And what's the internet without the rick-roll? Bar Do you pine for the days when men were men and wrote their own device drivers? Foo I have an ego the size of a small planet. I'm always right. This time I'm just even more right than usual. If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. Is "I hope you all die a painful death" too strong? Most days I wake up thinking I'm the luckiest bastard alive. My name is Linus Torvalds and I am your god. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. Length of Quotes: 0
請注意,兩個向量的內容是如何合併到 Target 向量中的,以及 Quotes 向量在合併後是如何變為空的。讓我們刪除 A_Sorter.Sort (Container => Foo_Bar); 行,看看會發生什麼
An infinite number of monkeys typing into GNU emacs would never make a good program. And what's the internet without the rick-roll? Foo Bar Do you pine for the days when men were men and wrote their own device drivers? I have an ego the size of a small planet. I'm always right. This time I'm just even more right than usual. If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. Is "I hope you all die a painful death" too strong? Most days I wake up thinking I'm the luckiest bastard alive. My name is Linus Torvalds and I am your god. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. Length of Quotes: 0
正如你所看到的,Target 向量不再是排序的。
在使用 Merge 時,務必記住這一點:Target 和 Source 向量在呼叫 Merge 之前都必須排序,否則你將得到一個未排序的向量作為結果。(如果你想要的只是這個結果,那麼使用以下過程之一可能更快: Append, Insert, Prepend。或者你可以簡單地使用 & 函式來完成任務。)
如前所述,可以使用多種不同的技術來連線兩個向量/向量元素,例如 append, prepend, insert 和 merge,但還有“&”運算子。& 函式的規格如下
function "&" (Left, Right : Vector) return Vector;
function "&" (Left : Vector; Right : Element_Type) return Vector;
function "&" (Left : Element_Type; Right : Vector) return Vector;
function "&" (Left, Right : Element_Type) return Vector;
你可以將一個向量連線到另一個向量,將元素連線到向量,將向量連線到元素,最後將兩個元素連線起來。所有 4 個函式都返回一個向量。
讓我們看看它是如何工作的。將此新增到程式的宣告部分
Foo : Vector;
Bar : Vector;
以及將其新增到主體中
-- This is similar to:
-- Foo.Append (To_Unbounded_String ("Foo 1"));
Foo := Foo & To_Unbounded_String ("Foo 1");
for i in Foo.First_Index .. Foo.Last_Index loop
SUIO.Put_Line (Item => Foo.Element (Index => i));
end loop;
IO.New_Line;
-- This is similar to:
-- Quotes.Append (Foo);
Quotes := Quotes & Foo;
for i in Quotes.First_Index .. Quotes.Last_Index loop
SUIO.Put_Line (Item => Quotes.Element (Index => i));
end loop;
IO.New_Line;
-- This is similar to:
-- Bar.Append (To_Unbounded_String ("Bar 1"));
-- Bar.Append (To_Unbounded_String ("Bar 2"));
Bar := To_Unbounded_String ("Bar 1") & To_Unbounded_String ("Bar 2");
for i in Bar.First_Index .. Bar.Last_Index loop
SUIO.Put_Line (Item => Bar.Element (Index => i));
end loop;
IO.New_Line;
-- This is similar to:
-- Foo.Prepend (To_Unbounded_String ("Foo 0"));
Foo := To_Unbounded_String ("Foo 0") & Foo;
for i in Foo.First_Index .. Foo.Last_Index loop
SUIO.Put_Line (Item => Foo.Element (Index => i));
end loop;
輸出如下:
Foo 1 I have an ego the size of a small planet. My name is Linus Torvalds and I am your god. Do you pine for the days when men were men and wrote their own device drivers? If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now. An infinite number of monkeys typing into GNU emacs would never make a good program. Is "I hope you all die a painful death" too strong? Most days I wake up thinking I'm the luckiest bastard alive. I'm always right. This time I'm just even more right than usual. And what's the internet without the rick-roll? Foo 1 Bar 1 Bar 2 Foo 0 Foo 1
雖然“&”運算子按預期工作,但有一個需要注意的地方,John Barnes 在他的 Ada 2005 書中對此進行了說明
結果相同,但由於涉及額外的複製,使用“&”效率較低。
讓我們嘗試一個簡單的例子,我們將 append 和 & 運算子進行比較。首先,我將完整地展示 & 的例子
with Ada.Containers.Vectors; use Ada.Containers;
procedure Somenumbers is
package Container is new Vectors (Natural, Natural);
use Container;
Numbers : Vector;
begin
for i in 0 .. 10000 loop
Numbers := Numbers & i;
end loop;
Numbers := Numbers & Numbers;
for i in 0 .. 10000 loop
Numbers := i & Numbers;
end loop;
end Somenumbers;
在我的計算機上對它進行計時,得到以下結果(10 次執行的平均值)
real 0m1.183s user 0m1.028s sys 0m0.156s
with Ada.Containers.Vectors; use Ada.Containers;
procedure Somenumbers is
package Container is new Vectors (Natural, Natural);
use Container;
Numbers : Vector;
begin
for i in 0 .. 10000 loop
Numbers.Append (New_Item => i);
end loop;
Numbers.Append (New_Item => Numbers);
for i in 0 .. 10000 loop
Numbers.Prepend (New_Item => i);
end loop;
end Somenumbers;
計時結果如下(10 次執行的平均值)
real 0m0.247s user 0m0.244s sys 0m0.002s
如果你關心效能,最好避免使用“&”函式。正如這個簡單的基準測試所顯示的,與使用“&”相比,有更快的連線向量和/或向量元素的方法。
使用“=”函式檢查兩個向量是否相等。只有當兩個向量長度相等,並且每個索引的元素完全相同時,它們才被視為相等。
要了解它是如何工作的,我們必須將此新增到程式的宣告部分
Foo : Vector;
以及將其新增到主體中
if Quotes = Foo then
IO.Put_Line (Item => "Quotes and Foo are equal");
else
IO.Put_Line (Item => "Quotes and Foo are NOT equal");
end if;
Foo.Append (New_Item => Quotes);
if Quotes = Foo then
IO.Put_Line (Item => "Quotes and Foo are equal");
else
IO.Put_Line (Item => "Quotes and Foo are NOT equal");
end if;
結果是
Quotes and Foo are NOT equal Quotes and Foo are equal
這當然是我們預期的結果。
-- Standard Ada library specification -- Copyright (c) 2003-2018 Maxim Reznik <reznikmm@gmail.com> -- Copyright (c) 2004-2016 AXE Consultants -- Copyright (c) 2004, 2005, 2006 Ada-Europe -- Copyright (c) 2000 The MITRE Corporation, Inc. -- Copyright (c) 1992, 1993, 1994, 1995 Intermetrics, Inc. -- SPDX-License-Identifier: BSD-3-Clause and LicenseRef-AdaReferenceManual -- -------------------------------------------------------------------------generictypeIndex_Typeisrange<>;typeElement_Typeisprivate;withfunction"=" (Left :inElement_Type; Right :inElement_Type)returnBooleanis<>;packageAda.Containers.VectorsispragmaPreelaborate (Vectors);subtypeExtended_IndexisIndex_Type'BaserangeIndex_Type'First - 1 .. Index_Type'Min (Index_Type'Base'Last - 1, Index_Type'Last) + 1; No_Index :constantExtended_Index := Extended_Index'First;typeVectoristaggedprivate;pragmaPreelaborable_Initialization (Vector);typeCursorisprivate;pragmaPreelaborable_Initialization (Cursor); Empty_Vector :constantVector; No_Element :constantCursor;function"=" (Left :inVector; Right :inVector)returnBoolean;functionTo_Vector (Length :inCount_Type)returnVector;functionTo_Vector (New_Item :inElement_Type; Length :inCount_Type)returnVector;function"&" (Left :inVector; Right :inVector)returnVector;function"&" (Left :inVector; Right :inElement_Type)returnVector;function"&" (Left :inElement_Type; Right :inVector)returnVector;function"&" (Left :inElement_Type; Right :inElement_Type)returnVector;functionCapacity (Container :inVector)returnCount_Type;procedureReserve_Capacity (Container :inoutVector; Capacity :inCount_Type);functionLength (Container :inVector)returnCount_Type;procedureSet_Length (Container :inoutVector; Length :inCount_Type);functionIs_Empty (Container :inVector)returnBoolean;procedureClear (Container :inoutVector);functionTo_Cursor (Container : Vector; Index : Extended_Index)returnCursor;functionTo_Index (Position :inCursor)returnExtended_Index;functionElement (Container :inVector; Index :inIndex_Type)returnElement_Type;functionElement (Position :inCursor)returnElement_Type;procedureReplace_Element (Container :inoutVector; Index :inIndex_Type; New_Item :inElement_Type);procedureReplace_Element (Container :inoutVector; Position :inCursor; New_item :inElement_Type);procedureQuery_Element (Container :inVector; Index :inIndex_Type; Process :notnullaccessprocedure(Element :inElement_Type));procedureQuery_Element (Position :inCursor; Process :notnullaccessprocedure(Element :inElement_Type));procedureUpdate_Element (Container :inoutVector; Index :inIndex_Type; Process :notnullaccessprocedure(Element :inoutElement_Type));procedureUpdate_Element (Container :inoutVector; Position :inCursor; Process :notnullaccessprocedure(Element :inoutElement_Type));procedureMove (Target :inoutVector; Source :inoutVector);procedureInsert (Container :inoutVector; Before :inExtended_Index; New_Item :inVector);procedureInsert (Container :inoutVector; Before :inCursor; New_Item :inVector);procedureInsert (Container :inoutVector; Before :inCursor; New_Item :inVector; Position :outCursor);procedureInsert (Container :inoutVector; Before :inExtended_Index; New_Item :inElement_Type; Count :inCount_Type := 1);procedureInsert (Container :inoutVector; Before :inCursor; New_Item :inElement_Type; Count :inCount_Type := 1);procedureInsert (Container :inoutVector; Before :inCursor; New_Item :inElement_Type; Position :outCursor; Count :inCount_Type := 1);procedureInsert (Container :inoutVector; Before :inExtended_Index; Count :inCount_Type := 1);procedureInsert (Container :inoutVector; Before :inCursor; Position :outCursor; Count :inCount_Type := 1);procedurePrepend (Container :inoutVector; New_Item :inVector);procedurePrepend (Container :inoutVector; New_Item :inElement_Type; Count :inCount_Type := 1);procedureAppend (Container :inoutVector; New_Item :inVector);procedureAppend (Container :inoutVector; New_Item :inElement_Type; Count :inCount_Type := 1);procedureInsert_Space (Container :inoutVector; Before :inExtended_Index; Count :inCount_Type := 1);procedureInsert_Space (Container :inoutVector; Before :inCursor; Position :outCursor; Count :inCount_Type := 1);procedureDelete (Container :inoutVector; Index :inExtended_Index; Count :inCount_Type := 1);procedureDelete (Container :inoutVector; Position :inoutCursor; Count :inCount_Type := 1);procedureDelete_First (Container :inoutVector; Count :inCount_Type := 1);procedureDelete_Last (Container :inoutVector; Count :inCount_Type := 1);procedureReverse_Elements (Container :inoutVector);procedureSwap (Container :inoutVector; I :inIndex_Type; J :inIndex_Type);procedureSwap (Container :inoutVector; I :inCursor; J :inCursor);functionFirst_Index (Container :inVector)returnIndex_Type;functionFirst (Container :inVector)returnCursor;functionFirst_Element (Container :inVector)returnElement_Type;functionLast_Index (Container :inVector)returnExtended_Index;functionLast (Container :inVector)returnCursor;functionLast_Element (Container :inVector)returnElement_Type;functionNext (Position :inCursor)returnCursor;procedureNext (Position :inoutCursor);functionPrevious (Position :inCursor)returnCursor;procedurePrevious (Position :inoutCursor);functionFind_Index (Container :inVector; Item :inElement_Type; Index :inIndex_Type := Index_Type'First)returnExtended_Index;functionFind (Container :inVector; Item :inElement_Type; Position :inCursor := No_Element)returnCursor;functionReverse_Find_Index (Container :inVector; Item :inElement_Type; Index :inIndex_Type := Index_Type'Last)returnExtended_Index;functionReverse_Find (Container :inVector; Item :inElement_Type; Position :inCursor := No_Element)returnCursor;functionContains (Container :inVector; Item :inElement_Type)returnBoolean;functionHas_Element (Position :inCursor)returnBoolean;procedureIterate (Container :inVector; Process :notnullaccessprocedure(Position :inCursor));procedureReverse_Iterate (Container :inVector; Process :notnullaccessprocedure(Position :inCursor));genericwithfunction"<" (Left :inElement_Type; Right :inElement_Type)returnBooleanis<>;packageGeneric_SortingisfunctionIs_Sorted (Container :inVector)returnBoolean;procedureSort (Container :inoutVector);procedureMerge (Target :inoutVector; Source :inoutVector);endGeneric_Sorting;privatetypeVectoristaggednullrecord; Empty_Vector :constantVector := (nullrecord);typeCursorisnullrecord; No_Element :constantCursor := (nullrecord);endAda.Containers.Vectors;
外部示例
[編輯原始碼]- 在以下位置搜尋
Ada.Containers.Vectors的示例:Rosetta Code,GitHub (gists),任何 Alire 包 或者 本華夏公益教科書。 - 在以下位置搜尋與
Ada.Containers.Vectors相關的帖子:Stack Overflow,comp.lang.ada 或 任何與 Ada 相關的頁面。
FSF GNAT
- 規格:a-convec.ads
- 主體:a-convec.adb
drake
