iterators_0.2.0_18995a4d/demos/src/iterators-demo-jsa_20.adb

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
with Ada.Numerics.Float_Random;
with Ada.Text_IO;

with Iterators.Std;
with Iterators.Text_IO;

procedure Iterators.Demo.JSA_20 is

   --  All necessary instantiations are in Iterators.Demo

   Rnd : Ada.Numerics.Float_Random.Generator;

   use Float_Iters.Linking;

   Data : aliased Float_Array_Iters.Element_Array :=
            (1 .. 1000 => Ada.Numerics.Float_Random.Random (Rnd));

   -------------
   -- Average --
   -------------

   procedure Average is
      Average : Float := 0.0;
   begin

      --  Manually compute an average

      for I in Data'Range loop
         Average := Average + Data (I);
      end loop;
      Average := Average / Float (Data'Length);

      --  same average using Reduce

      pragma Assert (Average =
                       (Float_Array_Iters.Const_Iter (Data)
                        & Float_Iters.Op.Reduce (0.0, "+"'Access))
                        / Float (Data'Length));
   end Average;

   -------------
   -- Top_Ten --
   -------------

   procedure Top_Ten is
      function Is_Candidate (Datum : Float) return Boolean is (Datum > 0.9);
      procedure Set_Winner (Datum : in out Float) is null;
   begin

      --  Plain loop

      for Element of Float_Iters.Iter -- Workaround for "of" bug -- see Iter decl
        (Float_Array_Iters.Iter (Data)
         & Float_Iters.Op.Filter (Is_Candidate'Access)
         & Float_Iters.Op.Take (10)).all
      loop
         Set_Winner (Element);
      end loop;

      --  Alternative: For_Each loop

      Float_Iters.Op.For_Each (Float_Array_Iters.Iter (Data)
                               & Float_Iters.Op.Filter (Is_Candidate'Access)
                               & Float_Iters.Op.Take (10),
                               Set_Winner'Unrestricted_Access);

   end Top_Ten;

   ---------------------------
   -- Print_Running_Average --
   ---------------------------

   procedure Print_Running_Average (Width : Positive := 3) is
      --  use Std.Strings.Linking;
      use Float2str.Linking;
      use Str2Float.Linking;
      use Float_Iters.Meta.Linking;

      use Float_Iters.Meta;
      use Float_Iters.Op;
      --  use Std.Strings.Op;

      use Float2str;
      use Str2float;

      function Div (F : Float) return Float is (F / Float (Width));
   begin
      Std.Strings.Op.For_Each
        --  (Just ("7") & "8" & "6" & "-1" & "12" & "15" & "-1" & "18"
        (Text_IO.Lines ("file_avg_demo.txt")  --  For each line
         & Map (Value'Access)                 --  Convert to Float
         & Filter (Is_Positive'Access)        --  Skip negative numbers
         & Window (Size => Width, Skip => 1)  --  Group into new iterator
         & Flat_Map (Scan (0.0, "+"'Access)   --     Sum subiterator
                     & Last)                  --     Take last (total sum)
         & Map (Div'Access)                   --  Divide by window size
         & Map (Image'Access),                --  Back to string
         GNAT.IO.Put_Line'Access);            --  Print running average
   end Print_Running_Average;

   --------------------------------------
   -- Print_Running_Average_Imperative --
   --------------------------------------
   --  Alternative using the Chain type instead of "&" concatenators
   procedure Print_Running_Average_Imperative (Width : Positive := 3) is
      S2F : Str2Float.Chain;
      Flt : Float_Iters.Operators.Chain;
      F2M : Float_Iters.Meta.Chain_To_Meta;
      M2F : Float_Iters.Meta.Chain_From_Meta;
      Sum : Float_Iters.Operators.Chain;
      Avg : Float_Iters.Operators.Chain;
      F2S : Float2Str.Chain;

      use Float_Iters.Op;

      function Div (F : Float) return Float is (F / Float (Width));
   begin
      S2F.Start (Text_IO.Lines ("file_avg_demo.txt"));
      S2F.Map (Value'Access);
      Flt.Resume (S2F);
      Flt.Filter (Is_Positive'Access);
      F2M.Resume (Flt);
      F2M.Window (Size => Width, Skip => 1);
      M2F.Resume (F2M);

      Sum.Start (Scan (0.0, "+"'Access));
      Sum.Continue (Last);
      M2F.Flat_Map (Sum.As_Iterator);

      Avg.Resume (M2F);
      Avg.Map (Div'Access);
      F2S.Resume (Avg);
      F2S.Map (Image'Access);
      F2S.For_Each (GNAT.IO.Put_Line'Access);
   end Print_Running_Average_Imperative;

   -------------------------------------
   -- Print_Running_Average_Classical --
   -------------------------------------
   --  Functionally equivalent classical implementation
   procedure Print_Running_Average_Classical (Width : Positive := 3) is
      use Ada.Text_IO;
      File : File_Type;
      Window : Float_Iters.Iterators.List;
      --  Any list-like type would do; we use this one that's already available
      --  instead of instantiating a new one.
   begin
      Open (File, In_File, "file_avg_demo.txt");

      while not End_Of_File (File) loop
         declare
            Line : constant String := Get_Line (File);
            Num  : constant Float  := Float'Value (Line);
         begin
            --  Skip negative numbers, used to signal invalid samples
            if Num >= 0.0 then
               --  Add new sample
               Window.Append (Num);
               --  Prune the window
               if Natural (Window.Length) > Width then
                  Window.Delete_First;
               end if;
               --  New averaged value?
               if Natural (Window.Length) = Width then
                  declare
                     Total : Float := 0.0;
                  begin
                     --  Compute total. This could be done more efficiently
                     --  by keeping a running total. However, that might have
                     --  numerical implications due to unneeded substractions,
                     --  would not be functionally equivalent to the Iterator
                     --  version, and would be less self-evident.
                     for Sample of Window loop
                        Total := Total + Sample;
                     end loop;
                     --  Write the new running average value
                     GNAT.IO.Put_Line (Float'Image (Total / Float (Width)));
                  end;
               end if;
            end if;
         end;
      end loop;

      Close (File);
   end Print_Running_Average_Classical;

   -----------------------------------
   -- Print_Running_Average_Ada202x --
   -----------------------------------

   procedure Print_Running_Average_Ada202x (Width : Positive := 3) is
      Window : Float_Iters.Iterators.List;
      --  Any list-like type would do; we use this one that's already available
      --  instead of instantiating a new one.
   begin
      for Line of Text_IO.Get_Lines ("file_avg_demo.txt") loop
         -- We would want to use a "when Float'Value (Line) >= 0" filter in the
         -- previous line, which is alas not yet implemented by GNAT.

         declare
            Num  : constant Float  := Float'Value (Line);
         begin
            --  Skip negative numbers, used to signal invalid samples
            if Num >= 0.0 then
               --  Add new sample
               Window.Append (Num);
               --  Prune the window
               if Natural (Window.Length) > Width then
                  Window.Delete_First;
               end if;
               --  New averaged value?
               if Natural (Window.Length) = Width then
                  declare
                     Total : Float := 0.0;
                  begin
                     --  Compute total. This could be done more efficiently
                     --  by keeping a running total. However, that might have
                     --  numerical implications due to unneeded substractions,
                     --  would not be functionally equivalent to the Iterator
                     --  version, and would be less self-evident.
                     for Sample of Window loop
                        Total := Total + Sample;
                     end loop;
                     --  Write the new running average value
                     GNAT.IO.Put_Line (Float'Image (Total / Float (Width)));
                  end;
               end if;
            end if;
         end;
      end loop;
   end Print_Running_Average_Ada202x;

begin
   Average;
   Top_Ten;

   GNAT.IO.Put_Line ("Iterators functional version");
   Print_Running_Average;

   GNAT.IO.Put_Line ("Iterators imperative version");
   Print_Running_Average_Imperative;

   GNAT.IO.Put_Line ("Classical no-iterators version");
   Print_Running_Average_Classical;

   GNAT.IO.Put_Line ("Ada 202x iterators version");
   Print_Running_Average_Ada202x;

end Iterators.Demo.JSA_20;