跳轉到內容

Ada 程式設計/庫/Ada.Containers.Vectors

來自華夏公益教科書

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

此語言功能僅從 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

[編輯 | 編輯原始碼]

學習如何使用 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_TypeElement_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 行,在這裡我們用引數NaturalUnbounded_String 例項化了Vectors 泛型。結果是一個向量,其中索引從 0 開始(記住NaturalInteger 的一個子型別,範圍為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/vectors_reserve_capacity.adb (view, plain text, download page, browse all)

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_LengthReplace_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/vectors_set_length.adb (view, plain text, download page, browse all)

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 可能最安全,因為你強制將一個有效的值放入每個元素。

檔案:vectors/vectors_to_vector.adb (檢視純文字下載頁面瀏覽所有)

插入資料和/或空元素

[編輯 | 編輯原始碼]

將資料插入向量是使用向量的關鍵部分。空向量通常沒有那麼有趣,所以讓我們看看如何向其中新增一些資料。

Vectors.Append

[編輯 | 編輯原始碼]

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

檔案:vectors/vectors_append.adb (檢視純文字下載頁面瀏覽所有)

Vectors.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);

PrependAppend 非常相似,唯一的區別是資料插入到向量的開頭而不是末尾。要檢視它的實際應用,請將以下內容新增到基本 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 也可以用來連線兩個向量。功能相同,唯一的區別是複製的是一個向量而不是單個元素。

檔案:vectors/vectors_prepend.adb (檢視純文字下載頁面瀏覽所有)

Vectors.Insert

[編輯 | 編輯原始碼]

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 庫的充分理由 - 對我來說,使用索引似乎更自然,更符合向量,但後來我瞭解到,實際上至少有兩個充分的理由可以使用遊標

  • 它們使通用演算法成為可能
  • 如果程式使用遊標建立,則可以輕鬆切換到其他序列容器

所以實際上至少有兩個充分的理由使用遊標。當然,如果你正在計算索引值,使用遊標不會給你帶來任何好處。像往常一樣,我們必須認真思考我們所需要的東西,並相應地選擇工具。

檔案:vectors/vectors_insert.adb (檢視純文字下載頁面瀏覽所有)

Vectors.Insert_Space

[編輯 | 編輯原始碼]

當你需要插入一堆“空”元素時,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_ErrorProgram_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 函式安全地讀取整個向量,而無需擔心會引發任何異常。

檔案:vectors/vectors_insert_space.adb (檢視純文字下載頁面瀏覽所有)

讀取和查詢向量

[編輯 | 編輯原始碼]

與向向量新增資料一樣重要的是能夠讀取資料,所以讓我們來看看一些可用於執行此操作的方法。

Vectors.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 包中大多數與遊標相關的過程和函式在內部將遊標轉換為索引,然後使用索引過程/函式進行實際的繁重處理。因此,我通常避免使用遊標形式。我認為索引更易於使用和理解。使用遊標形式的主要好處是靈活性。如果您使用遊標,您的程式碼將在很少修改的情況下與其他基於序列的容器一起工作,例如 雙向連結串列

檔案:vectors/vectors_element.adb (檢視純文字下載頁面瀏覽所有)

Vectors.Query_Element

[編輯 | 編輯原始碼]

以下是 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_ElementElement 函式既相似又不同。Element 返回給定索引的 Element_Type,而 Query_ElementElement_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_ElementElement 速度更快,但是使用回撥過程的混亂程度可能不值得原始碼變得混亂,除非效能至關重要。元素越大,使用 Element 時效能下降就越嚴重。

檔案:vectors/vectors_query_element.adb (檢視純文字下載頁面瀏覽所有)

修改 - 如何“修改”元素

[編輯 | 編輯原始碼]

更改向量中的資料是一項常見任務。以下兩種方法可以實現這一點。

Vectors.Update_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,我們將在後面討論它。

檔案:vectors/vectors_update_element.adb (檢視純文字下載頁面瀏覽所有)

Vectors.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 則完全替換給定索引上的元素,並且與 ElementQuery_Element 一樣,Update_ElementReplace_Element 速度更快,但是它會使用回撥過程使原始碼變得混亂。因此,與許多其他事情一樣,這是乾淨程式碼與效能之間的權衡,所以一定要測試並選擇最適合專案的方案。

檔案:vectors/vectors_replace_element.adb (檢視純文字下載頁面瀏覽所有)

刪除和清除

[編輯 | 編輯原始碼]

當然,可以從向量中刪除元素,並完全清除向量。我敢打賭,細心的讀者已經猜到了這些過程的名稱。然而,也有一些意外。

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/vectors_clear.adb (view, plain text, download page, browse all)

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/vectors_delete.adb (view, plain text, download page, browse all)

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/vectors_delete_first.adb (view, plain text, download page, browse all)

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 相同。

檔案: vectors/vectors_delete_last.adb (view, plain text, download page, browse all)

移動、交換和反轉

[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/vectors_move.adb (view, plain text, download page, browse all)

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/vectors_reverse_elements.adb (view, plain text, download page, browse all)

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 過程將位置 IJ 的值相互交換,這意味著位置 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 程式中。請注意,我這裡僅僅使用氣泡排序作為示例,您不應該使用這種排序方法來排序您的資料,因為它非常慢且效率低下。

檔案: vectors/vectors_swap.adb (view, plain text, download page, browse all)

關於向量的資訊

[編輯 | 編輯原始碼]

可以使用以下方法獲取有關給定向量的相當多的資訊。

Vectors.Capacity

[編輯 | 編輯原始碼]

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 的冪增長,而長度是實際新增資料的線性計數。

檔案:vectors/vectors_capacity.adb (檢視純文字下載頁面瀏覽所有)

Vectors.Is_Empty

[編輯 | 編輯原始碼]

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**。

檔案:vectors/vectors_is_empty.adb (檢視純文字下載頁面瀏覽所有)

Vectors.Length

[編輯 | 編輯原始碼]

以下是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

檔案:vectors/vectors_length.adb (檢視純文字下載頁面瀏覽所有)

遍歷向量

[編輯 | 編輯原始碼]

遍歷向量可以使用簡單的迴圈來完成,但為什麼不使用內建方法呢?這些方法不如定製迴圈靈活,但如果我們只需要一個簡單的“陣列遍歷”,那麼這些方法就是最好的選擇。

Vectors.Iterate

[編輯 | 編輯原始碼]

使用索引或遊標遍歷向量非常容易,但還有一個更簡單的方法:Iterate 過程。

以下是 Iterate 的規範

procedure Iterate
  (Container : Vector;
   Process   : not null access procedure (Position : Cursor));

Iterate 使您能夠將 Process 過程應用於向量中的每個元素。與 Query_ElementUpdate_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 過程,這自然限制了它的一些用法。

檔案:vectors/vectors_iterate.adb (檢視純文字下載頁面瀏覽所有)

Vectors.Reverse_Iterate

[編輯 | 編輯原始碼]

以下是 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 過程一起使用。

檔案:vectors/vectors_reverse_iterate.adb (檢視純文字下載頁面瀏覽所有)

向量中的第一個和最後一個位置

[編輯 | 編輯原始碼]

無論您是使用遊標還是索引,有時您都需要識別向量的開頭和/或結尾。為此特定工作,我們有 4 種方法可用。讓我們來看看它們。

Vectors.First_Index

[編輯 | 編輯原始碼]

以下是 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 更加安全。

檔案:vectors/vectors_first_index.adb (檢視純文字下載頁面瀏覽所有)

Vectors.Last_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 為令人厭煩,但它是一個應該意識到並理解的怪事。

檔案:vectors/vectors_last_index.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.First

[編輯 | 編輯原始碼]

以下是 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_IndexFirst 來識別向量中的第一個位置。您**絕不**應該硬編碼第一個位置的數值,因為這種方法在您更改向量的 Index_Type 時有失敗的風險。

檔案:vectors/vectors_first.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.Last

[編輯 | 編輯原始碼]

以下是 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

檔案:vectors/vectors_last.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

使用遊標進行下一個和上一個

[編輯 | 編輯原始碼]

我們已經瞭解瞭如何使用 IterateReverse_Iterate 和使用索引的常規迴圈來迭代向量,因此我認為現在該學習如何使用遊標來完成它了。為此,我們提供了 NextPrevious 方法。

Vectors.Next

[編輯 | 編輯原始碼]

以下是 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

希望進一步解釋是不必要的。

檔案:vectors/vectors_next.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.Previous

[編輯 | 編輯原始碼]

以下是 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

這裡應該沒有驚喜。

檔案:vectors/vectors_previous.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

查詢東西

[編輯 | 編輯原始碼]

找出特定元素是否存在於向量中,或者給定位置是否包含值,這是在使用向量時相當常見的任務,因此,我們提供了一些有價值的工具。

Vectors.Find_Index

[編輯 | 編輯原始碼]

以下是 Find_Index 的規範

function Find_Index
  (Container : Vector;
   Item      : Element_Type;
   Index     : Index_Type := Index_Type'First) return Extended_Index;

Find_Index 函式實際上只是圍繞一個簡單迴圈的包裝器,其中向量中的每個元素都與 Item 進行比較。它沒有任何“神奇”之處。它不會比自制迴圈快,但可以為您節省幾行程式碼。Find_IndexIndex 開始向前搜尋向量。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_IndexIndex 引數可以讓我們從指定索引開始搜尋。
  • 如果未找到匹配的元素,則返回 No_Index 值。

除了這三件事之外,關於 Find_Index 真的沒什麼好說的了。

檔案:vectors/vectors_find_index.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.Find

[編輯 | 編輯原始碼]

在這個示例中,您將找到上面的 Find_Index 示例,它被修改為使用 Find。這兩個函式在使用和功能方面非常相似,因此,進一步解釋完全是浪費空間。所以您只得到規範和示例。

以下是 Find 的規範

function Find
  (Container : Vector;
   Item      : Element_Type;
   Position  : Cursor := No_Element) return Cursor;
檔案: vectors/vectors_find.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.Reverse_Find_Index

[編輯 | 編輯原始碼]

Reverse_Find_IndexFind_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;
檔案: vectors/vectors_reverse_find_index.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.Reverse_Find

[編輯 | 編輯原始碼]

Reverse_FindFind 類似,區別在於它從 Position 開始反向搜尋,直到到達向量開頭或找到匹配項。在示例原始碼頁面上,您會發現 Find 示例被修改以反映這一點。

Reverse_Find 的規範與 Find 驚人地相似

function Reverse_Find
  (Container : Vector;
   Item      : Element_Type;
   Position  : Cursor := No_Element) return Cursor;
檔案: vectors/vectors_reverse_find.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.Contains

[編輯 | 編輯原始碼]

這是 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 函式。這樣的函式很容易實現

檔案: vectors/vectors_reverse_contains.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

. 由於兩個 Find 函式都提供了 Reverse_ 版本,我發現沒有提供 Reverse_Contains 函式很奇怪。幸運的是,如前面的原始碼所示,很容易自己新增它。

檔案: vectors/vectors_contains.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.Has_Element

[編輯 | 編輯原始碼]

這是 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,情況就不同了,因為導航雙向連結串列的唯一方法是使用遊標。

檔案: vectors/vectors_has_element.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

從索引到遊標,反之亦然

[編輯 | 編輯原始碼]

有時需要將索引轉換為遊標,反之亦然。這可以使用名為 To_CursorTo_Index 的函式來完成。

Vectors.To_Cursor

[編輯 | 編輯原始碼]

這是 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

檔案: vectors/vectors_to_cursor.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.To_Index

[編輯 | 編輯原始碼]

這是 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

檔案: vectors/vectors_to_index.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Vectors.Generic_Sorting

[編輯 | 編輯原始碼]

向量包還包含用於對向量進行排序的工具,以泛型包 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 程式的宣告部分,您就可以開始使用。在下文中,我們將瞭解如何使用泛型過程來對向量進行排序。

Generic_Sorting.Sort

[編輯 | 編輯原始碼]

這是 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 檔案。比這更簡單的排序方式沒有了吧。

檔案: vectors/vectors_generic_sorting.sort.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Generic_Sorting.Is_Sorted

[編輯 | 編輯原始碼]

以下是 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

檔案: vectors/vectors_generic_sorting.is_sorted.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

Generic_Sorting.Merge

[編輯 | 編輯原始碼]

以下是 Merge 的規格

procedure Merge (Target : in out Vector; Source : in out Vector);

Merge 很特別。它將 TargetSource 合併,並將 Source 設為空。特別之處在於它不會對結果向量進行排序,而這正是我們對 Merge 的預期,因為它是 Generic_Sorting 的一部分。如果你希望 Merge 的結果是一個排序的向量,那麼在呼叫 Merge 之前,你必須對 TargetSource 進行排序。

讓我們看看它是如何工作的。將此新增到 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 時,務必記住這一點:TargetSource 向量在呼叫 Merge 之前都必須排序,否則你將得到一個未排序的向量作為結果。(如果你想要的只是這個結果,那麼使用以下過程之一可能更快: Append Insert Prepend。或者你可以簡單地使用 & 函式來完成任務。)

檔案: vectors/vectors_generic_sorting.merge.adb (檢視, 純文字, 下載頁面, 瀏覽所有)

使用 & 運算子進行連線

[編輯 | 編輯原始碼]

如前所述,可以使用多種不同的技術來連線兩個向量/向量元素,例如 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

現在使用 append prepend

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
-- -------------------------------------------------------------------------

generic
   type Index_Type is range <>;
   type Element_Type is private;

   with function "=" (Left  : in Element_Type;
                      Right : in Element_Type)
          return Boolean is <>;

package Ada.Containers.Vectors is

   pragma Preelaborate (Vectors);

   subtype Extended_Index is Index_Type'Base
     range Index_Type'First - 1
             .. Index_Type'Min (Index_Type'Base'Last - 1, Index_Type'Last) + 1;

   No_Index : constant Extended_Index := Extended_Index'First;

   type Vector is tagged private;
   pragma Preelaborable_Initialization (Vector);

   type Cursor is private;
   pragma Preelaborable_Initialization (Cursor);

   Empty_Vector : constant Vector;

   No_Element : constant Cursor;

   function "=" (Left  : in Vector;
                 Right : in Vector)
     return Boolean;

   function To_Vector (Length : in Count_Type) return Vector;

   function To_Vector (New_Item : in Element_Type;
                       Length   : in Count_Type)
     return Vector;

   function "&" (Left  : in Vector;
                 Right : in Vector)
     return Vector;

   function "&" (Left  : in Vector;
                 Right : in Element_Type)
     return Vector;

   function "&" (Left  : in Element_Type;
                 Right : in Vector)
     return Vector;

   function "&" (Left  : in Element_Type;
                 Right : in Element_Type)
     return Vector;

   function Capacity (Container : in Vector) return Count_Type;

   procedure Reserve_Capacity (Container : in out Vector;
                               Capacity  : in     Count_Type);

   function Length (Container : in Vector) return Count_Type;

   procedure Set_Length (Container : in out Vector;
                         Length    : in     Count_Type);

   function Is_Empty (Container : in Vector) return Boolean;

   procedure Clear (Container : in out Vector);

   function To_Cursor (Container : Vector;
                       Index     : Extended_Index)
     return Cursor;

   function To_Index (Position : in Cursor) return Extended_Index;

   function Element (Container : in Vector;
                     Index     : in Index_Type)
     return Element_Type;

   function Element (Position : in Cursor) return Element_Type;

   procedure Replace_Element (Container : in out Vector;
                              Index     : in     Index_Type;
                              New_Item  : in     Element_Type);

   procedure Replace_Element (Container : in out Vector;
                              Position  : in     Cursor;
                              New_item :  in     Element_Type);

   procedure Query_Element
    (Container : in Vector;
     Index     : in Index_Type;
     Process   : not null access procedure (Element : in Element_Type));

   procedure Query_Element
    (Position : in Cursor;
     Process  : not null access procedure (Element : in Element_Type));

   procedure Update_Element
    (Container : in out Vector;
     Index     : in     Index_Type;
     Process   : not null access procedure (Element : in out Element_Type));

   procedure Update_Element
    (Container : in out Vector;
     Position  : in      Cursor;
     Process   : not null access procedure (Element : in out Element_Type));

   procedure Move (Target : in out Vector;
                   Source : in out Vector);

   procedure Insert (Container : in out Vector;
                     Before    : in     Extended_Index;
                     New_Item  : in     Vector);

   procedure Insert (Container : in out Vector;
                     Before    : in     Cursor;
                     New_Item  : in     Vector);

   procedure Insert (Container : in out Vector;
                     Before    : in     Cursor;
                     New_Item  : in     Vector;
                     Position  :    out Cursor);

   procedure Insert (Container : in out Vector;
                     Before    : in     Extended_Index;
                     New_Item  : in     Element_Type;
                     Count     : in     Count_Type := 1);

   procedure Insert (Container : in out Vector;
                     Before    : in     Cursor;
                     New_Item  : in     Element_Type;
                     Count     : in     Count_Type := 1);

   procedure Insert (Container : in out Vector;
                     Before    : in     Cursor;
                     New_Item  : in     Element_Type;
                     Position  :    out Cursor;
                     Count     : in     Count_Type := 1);

   procedure Insert (Container : in out Vector;
                     Before    : in     Extended_Index;
                     Count     : in     Count_Type := 1);

   procedure Insert (Container : in out Vector;
                     Before    : in     Cursor;
                     Position  :    out Cursor;
                     Count     : in     Count_Type := 1);

   procedure Prepend (Container : in out Vector;
                      New_Item  : in     Vector);

   procedure Prepend (Container : in out Vector;
                      New_Item  : in     Element_Type;
                      Count     : in     Count_Type := 1);

   procedure Append (Container : in out Vector;
                     New_Item  : in     Vector);

   procedure Append (Container : in out Vector;
                     New_Item  : in     Element_Type;
                     Count     : in     Count_Type := 1);

   procedure Insert_Space (Container : in out Vector;
                           Before    : in     Extended_Index;
                           Count     : in     Count_Type := 1);

   procedure Insert_Space (Container : in out Vector;
                           Before    : in     Cursor;
                           Position  :    out Cursor;
                           Count     : in     Count_Type := 1);

   procedure Delete (Container : in out Vector;
                     Index     : in     Extended_Index;
                     Count     : in     Count_Type := 1);

   procedure Delete (Container : in out Vector;
                     Position  : in out Cursor;
                     Count     : in     Count_Type := 1);

   procedure Delete_First (Container : in out Vector;
                           Count     : in     Count_Type := 1);

   procedure Delete_Last (Container : in out Vector;
                          Count     : in     Count_Type := 1);

   procedure Reverse_Elements (Container : in out Vector);

   procedure Swap (Container : in out Vector;
                   I         : in     Index_Type;
                   J         : in     Index_Type);

   procedure Swap (Container : in out Vector;
                   I         : in     Cursor;
                   J         : in     Cursor);

   function First_Index (Container : in Vector) return Index_Type;

   function First (Container : in Vector) return Cursor;

   function First_Element (Container : in Vector) return Element_Type;

   function Last_Index (Container : in Vector) return Extended_Index;

   function Last (Container : in Vector) return Cursor;

   function Last_Element (Container : in Vector) return Element_Type;

   function Next (Position : in Cursor) return Cursor;

   procedure Next (Position : in out Cursor);

   function Previous (Position : in Cursor) return Cursor;

   procedure Previous (Position : in out Cursor);

   function Find_Index (Container : in Vector;
                        Item      : in Element_Type;
                        Index     : in Index_Type := Index_Type'First)
     return Extended_Index;

   function Find (Container : in Vector;
                  Item      : in Element_Type;
                  Position  : in Cursor := No_Element)
     return Cursor;

   function Reverse_Find_Index (Container : in Vector;
                                Item      : in Element_Type;
                                Index     : in Index_Type := Index_Type'Last)
     return Extended_Index;

   function Reverse_Find (Container : in Vector;
                          Item      : in Element_Type;
                          Position  : in Cursor := No_Element)
     return Cursor;

   function Contains (Container : in Vector;
                      Item      : in Element_Type)
     return Boolean;

   function Has_Element (Position : in Cursor) return Boolean;

   procedure Iterate
    (Container : in Vector;
     Process   : not null access procedure (Position : in Cursor));

   procedure Reverse_Iterate
     (Container : in Vector;
      Process   : not null access procedure (Position : in Cursor));

   generic
      with function "<" (Left  : in Element_Type;
                         Right : in Element_Type)
             return Boolean is <>;

   package Generic_Sorting is

      function Is_Sorted (Container : in Vector) return Boolean;

      procedure Sort (Container : in out Vector);

      procedure Merge (Target : in out Vector;
                       Source : in out Vector);

   end Generic_Sorting;

private

   type Vector is tagged null record;

   Empty_Vector : constant Vector := (null record);

   type Cursor is null record;

   No_Element : constant Cursor := (null record);

end Ada.Containers.Vectors;

另請參見

[編輯 | 編輯原始碼]

華夏公益教科書

[編輯 | 編輯原始碼]

外部示例

[編輯原始碼]

Ada 2005 理念

[編輯 | 編輯原始碼]

Ada 參考手冊

[編輯 | 編輯原始碼]

開源實現

[編輯 | 編輯原始碼]

FSF GNAT

drake

華夏公益教科書