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