2016-01-19 1 views
1

Ada 2012 user defined iterator

Эта функция позволяет пользователю создавать пользовательский итератор. Вместо того, чтобы писать Get (List, Index), можно написать List (Index) и вместо того, чтобы писать for Index in 1 .. List.Max можно написать for Index in List'Range. Я не уверен, что если List'Range возможен, если список не является массивом, может быть, существует еще один синтаксис для определяемого пользователем итератора.Как использовать итераторы, определенные пользователем Ada 2012?

  • Как использовать итераторы, определенные пользователем Ada 2012?
  • Предпочтительно: как реализовать пользовательский итератор в примере?

Пример

Это стек или пример LIFO. Следующим шагом является скрыть type Stack членов и реализовать пользовательский итератор для type StackList.

with Ada.Text_IO; 
with Ada.Integer_Text_IO; 

procedure Main is 

    use Ada.Text_IO; 
    use Ada.Integer_Text_IO; 

    type Integer_Array is array (Integer range <>) of Integer; 

    type Stack (Count : Natural) is record 
     List : Integer_Array (1 .. Count); 
     Top : Natural := 0; 
    end record; 

    procedure Push (Item : Integer; Result : in out Stack) is 
    begin 
     Result.Top := Result.Top + 1; 
     Result.List (Result.Top) := Item; 
    end; 

    S : Stack (10); 

begin 

    Push (5, S); 
    Push (3, S); 
    Push (8, S); 

    for I in S.List'First .. S.Top loop 
     Put (S.List (I), 2); 
     New_Line; 
    end loop; 

    --for I in S'Range loop 
     --Put (S (I), 2); 
     --New_Line; 
    --end loop; 
end; 
+0

Можете ли вы использовать экземпляр 'Ada.Containers.Vectors'? См. Также [* Обоснование для Ada 2012: 6.3 Итерация *] (http://www.ada-auth.org/standards/12rat/html/Rat12-6-3.html). – trashgod

+0

См. [Этот ответ] (http://stackoverflow.com/a/11797416/40851). –

ответ

1

Вы можете использовать итераторы в for петли так:

for C in S.Iterator loop 
    Put (S (C)); 
    New_Line; 
end loop; 

Или в более ленивой версии:

for E of S loop 
    Put (E); 
    New_Line; 
end loop; 

Реализация итератора является довольно длинная история, и я имею в вы к Ada 2012 rationale (который также упоминал @trashgod) для подробного объяснения.

+1

Вероятно, вы имели в виду «для C в цикле S.Iterate», а затем только если S определил правильные аспекты, как в случае стандартных контейнеров, но не для одного из исходного вопроса. – manuBriot

+0

Я уверен, что я могу назвать функцию, возвращающую итератор, что мне нравится (даже что-то бесчувственное). Но да, если 'S' был из' Ada.Containers', он должен был быть «для C в S.Iterate». –

3

Использование S'Range не предназначено для контейнеров, только для стандартных массивов. Это невозможно изменить.

Возможно, вы должны были сказать for C in S.Iterate loop, который вызывает функцию Iterate и возвращает курсоры для каждого шага цикла. В этот момент, вы должны использовать Element (C), чтобы получить доступ к реальному элементу (или, возможно, более эффективно Reference (C).

Третья версия for E of S loop, которая возвращает непосредственно элемент. Вы не имеете доступа к соответствующему курсором. Это в общем случае предпочтительный способ записи цикла, за исключением, возможно, при повторении всего содержимого карты, поскольку нет доступа к ключу, только значение.

Для получения дополнительной информации о том, как добавить поддержку для этих петель в ваших собственных структурах данных, вы можете взглянуть на два драгоценных камня, опубликованных AdaCore:

, который должен предоставить всю необходимую информацию, я надеюсь.

1

Я не уверен, что возможность перебора содержимого стека является частью обычного использования для стеков!Это в стороне, это своего рода код, который вы можете написать с помощью обобщенной итерации (ARM 5.5.2):

with Ada.Text_IO; 
with Stacks; 
procedure Iteration is 
    use Ada.Text_IO; 
    S : Stacks.Stack (10); 
begin 
    Stacks.Push (S, 5); 
    Stacks.Push (S, 3); 
    Stacks.Push (S, 8); 

    for C in Stacks.Iterate (S) loop 
     Put_Line (Stacks.Element (C)'Img);           --' 
    end loop; 
end Iteration; 

Это возможно спецификации для Stacks. Обратите внимание, что System используется только в частной части.

with Ada.Iterator_Interfaces; 
private with System; 
package Stacks is 

Это часть фундаментальной абстракции стека. Обратите внимание: если вы хотите иметь возможность индексированного доступа (My_Stack (42) или цикл for E of My_Stack loop...), все становится намного сложнее. Для начала необходимо будет пометить Stack.

type Stack (Count : Natural) is private; 

    procedure Push (To : in out Stack; Item : Integer); 

    function Element (S : Stack; Index : Positive) return Integer; 

Остальная часть публичной части предназначена для поддержки обобщенной итерации.

type Cursor is private; 
    function Has_Element (Pos : Cursor) return Boolean; 
    function Element (C : Cursor) return Integer; 

    package Stack_Iterators 
    is new Ada.Iterator_Interfaces (Cursor, Has_Element); 

    function Iterate (S : Stack) 
        return Stack_Iterators.Forward_Iterator’Class;     --' 
private                    --' 
    type Integer_Array is array (Positive range <>) of Integer; 

    type Stack (Count : Natural) is record 
     List : Integer_Array (1 .. Count); 
     Top : Natural := 0; 
    end record; 

    function Element (S : Stack; Index : Positive) return Integer 
    is (S.List (Index)); 

Cursor необходима ссылка на объект это курсор для. Я использовал System.Address, чтобы избежать использования типов доступа и вам нужно сделать Stack s aliased.

type Cursor is record 
     The_Stack : System.Address; 
     List_Index : Positive := 1; 
    end record; 
end Stacks; 

Пакет тело ...

with System.Address_To_Access_Conversions; 
package body Stacks is 
    procedure Push (To : in out Stack; Item : Integer) is 
    begin 
     To.Top := To.Top + 1; 
     To.List (To.Top) := Item; 
    end Push; 

Мы будем иметь, чтобы преобразовать Stack адреса для указателей-to Stack s.

package Address_Conversions 
    is new System.Address_To_Access_Conversions (Stack); 

    function Has_Element (Pos : Cursor) return Boolean is 
     S : Stack renames Address_Conversions.To_Pointer (Pos.The_Stack).all; 
    begin 
     return Pos.List_Index <= S.Top; 
    end Has_Element; 

    function Element (C : Cursor) return Integer is 
     S : Stack renames Address_Conversions.To_Pointer (C.The_Stack).all; 
    begin 
     return S.List (C.List_Index); 
    end Element; 

Теперь для настоящего итератора; Я только сделал форвардную версию.

type Iterator is new Stack_Iterators.Forward_Iterator with record 
     The_Stack : System.Address; 
    end record; 
    overriding function First (Object : Iterator) return Cursor; 
    overriding function Next (Object : Iterator; Pos : Cursor) return Cursor; 

    function Iterate (S : Stack) 
        return Stack_Iterators.Forward_Iterator'Class is     --' 
    begin 
     -- I seem to be relying on S being passed by reference. 
     -- This would be OK anyway if Stack was tagged; but it is a 
     -- reasonable bet that the compiler will pass a large object 
     -- by reference. 
     return It : Iterator do 
     It.The_Stack := S’Address;              --' 
     end return;                 --' 
    end Iterate; 

    function First (Object : Iterator) return Cursor is 
    begin 
     return C : Cursor do 
     C.The_Stack := Object.The_Stack; 
     C.List_Index := 1; 
     end return; 
    end First; 

    function Next (Object : Iterator; Pos : Cursor) return Cursor is 
     pragma Unreferenced (Object); 
    begin 
     return C : Cursor do 
     C.The_Stack := Pos.The_Stack; 
     C.List_Index := Pos.List_Index + 1; 
     end return; 
    end Next; 
end Stacks; 
+0

Я сделал [demo] (https://ideone.com/urgKhq). – Jossi

 Смежные вопросы

  • Нет связанных вопросов^_^