comp.lang.ada
 help / color / mirror / Atom feed
From: Georg Bauhaus <rm.dash-bauhaus@futureapps.de>
Subject: Re: Ada2012 : In praise of 'for ... of ... loop'...
Date: Thu, 31 May 2012 00:26:02 +0200
Date: 2012-05-31T00:25:55+02:00	[thread overview]
Message-ID: <4fc69e72$0$6633$9b4e6d93@newsspool2.arcor-online.net> (raw)
In-Reply-To: <371a4b67-1969-4cd5-90f4-d58a9b276f29@googlegroups.com>

On 30.05.12 10:10, Martin wrote:

> That's 54 lines down to 29 lines, no 'Access and it's perfectly clear what's happening and it happens in a single subprogram. No need to talk about Cursor or calls to Element (C)...
>
> ...It also enhances the value of other nested subprograms - they're probably doing something more interesting, so I don't mind the comment-box headers for them as much as I used to.
>
> All thanks to a little syntatic sugar.
>
> To my eyes, this a big win.

To my eyes also. It is really nice that Iterate and the "loop
body subprograms" are not torn apart. That seems to outweigh
the loss of naming opportunities.


I'm still unsure if sweetening adds clarity when one has to write for
"the other side" of syntax sugar. For example, when providing a
concrete Forward_Iterator from Ada.Iterator_Interfaces.
However, because writing a Forward_Iterator seems to require more
types (and packaging) in place of juggling subprograms from the
container types, that might be a win too, even at the price of
added LOC.

As an example, I have tried a "functional" Reduce function written in
2005 style and in 2012 style, special in that it is required to
operate on a span of the values only, not the whole container,
mimicking Floor and Ceiling. In pseudo code,

   "+"/(First .. Last),

While the 2012 version of Reduce is pleasantly clear and a little shorter,
writing the "backstage stuff" had, at first, seemed to spoil the effort.

In Ada 2012, there is an Iterate function in the 2012 Vectors that
takes a parameter Start : Cursor, but not a corresponding Finish : Cursor.

So I made a Forward_Iterator that would know Start and Finish.
It has a construction function and overriding First and Next.
This  made the whole program quite a bit longer.
Not necessarily worse, I think, but longer.
Perhaps more "rigorous", or "typish"?

I hope the any unnecessary lengthening is caused by my limited
understanding. Or that the type centric approach of 2012 Forward_Iterator
is better, more clear, and more modular in the end.


Package Iterations_05 below is the 2005 version of the Reduce part.
I think the approach follows standard STL style programming.

generic
    type Element_Type is private;
    type Cursor is private;
    with function Next (Position : Cursor) return Cursor is <>;
    with function Element (Position : Cursor) return Element_Type is <>;
package Iterations_05 is

    generic
       Zero : in Element_Type;
       with function Binop (Left, Right : in Element_Type) return Element_Type;
    function Reduce_05 (First, One_After : Cursor) return Element_Type;

end Iterations_05;


package body Iterations_05 is

    function Reduce_05 (First, One_After : Cursor) return Element_Type is
       Result : Element_Type;
       Position : Cursor;
    begin
       Result := Zero;
       Position := First;
       loop
          exit when Position = One_After;
          Result := Binop (Result, Element (Position));
          Position := Next (Position);
       end loop;
       return Result;
    end Reduce_05;

end Iterations_05;


After instantiating the two generics, one picks cursors for the
endpoints of the "intervals" to be taken from a container.
(At 1 and 50, and 51 and 100 in this case.)

with Ada.Text_IO;
with Iterations_05;
with Num_Types, Num_Vecs;
procedure Test_Iterations_05 is

    use Num_Vecs;

    package Functional is new Iterations_05
      (Element_Type => Num_Types.Nat,
       Cursor => Num_Vecs.Cursor);

    use type Num_Types.Nat;

    function Sum is new Functional.Reduce_05
      (Zero => 0,
       Binop => "+");

    package Num_IO is new Ada.Text_IO.Integer_IO (Num_Types.Nat);

    List : Num_Vecs.Vector;
begin
    for K in Num_Types.Nat range 1 .. 100 loop
       List.Append (K);
    end loop;
    Num_IO.Put (Sum (First     => First (List),
                     One_After => Next (To_Cursor (List,  50))));
    Num_IO.Put (Sum (First     => To_Cursor (List, 51),
                     One_After => Next (Last (List))));
end Test_Iterations_05;


That's it.

The 2012 version of the "functional" part Iterations_2012 is just a little
shorter than its 2005 counterpart. The body of the Reduce function
uses a sweetened for loop:


with Ada.Iterator_Interfaces;
generic
    type Element_Type is private;
    with package Iteration is new Ada.Iterator_Interfaces (<>);
    with function Element (Position : Iteration.Cursor) return Element_Type;
package Iterations_2012 is

    generic
       Zero : in Element_Type;
       with function Binop (Left, Right : in Element_Type) return Element_Type;
    function Reduce_2012 (Span : Iteration.Forward_Iterator'class) return Element_Type;

end Iterations_2012;


package body Iterations_2012 is

    function Reduce_2012 (Span : Iteration.Forward_Iterator'class) return Element_Type is
       Result : Element_Type;
    begin
       Result := Zero;
       for k in Span loop
          Result := Binop (Result, Element (k));
       end loop;
       return Result;
    end Reduce_2012;

end Iterations_2012;


Since a sweetened for loop should be used, I though that passing a "slice"
of a container could only be achieved by a Forward_Iterator. Is this correct?
Note the lengthy package called Splitting that provides it.


with Ada.Iterator_Interfaces;
with Num_Types, Num_Vecs;
with Iterations_2012;
with Ada.Text_IO;
procedure Test_Iterations_2012 is

    use Num_Vecs;
    
    package Split_Iterators is new Ada.Iterator_Interfaces
      (Cursor => Num_Vecs.Cursor,
       Has_Element => Num_Vecs.Has_Element);
    
    package Splitting is
       use Split_Iterators;
    
       type Split_Iterator is
	 limited new Split_Iterators.Forward_Iterator with
	 record
	   First, One_After : Num_Vecs.Cursor;
	 end record;
    
       function Make_Iterator
         (First, One_After : Num_Vecs.Cursor) return Split_Iterator;
       overriding function First (Object : Split_Iterator) return Cursor;
       overriding function Next
	(Object : Split_Iterator; Position: Cursor) return Cursor;
    end Splitting;
    
    package body Splitting is
       function Make_Iterator
	(First, One_After : Num_Vecs.Cursor) return Split_Iterator is
       begin
	 return (Split_Iterators.Forward_Iterator with First, One_After);
       end Make_Iterator;
       
       overriding function First (Object : Split_Iterator) return Cursor is
       begin
	 return Object.First;
       end First;
    
       overriding function Next
	(Object : Split_Iterator; Position: Cursor) return Cursor is
       begin
	 if Position = Object.One_After then
	    return Num_Vecs.No_Element;
	 else
	    return Num_Vecs.Next (Position);
	 end if;
       end Next;
    end Splitting;
    
    package Functional is new Iterations_2012
      (Element_Type => Num_Types.Nat,
       Element => Num_Vecs.Element,
       Iteration => Split_Iterators);

    use type Num_Types.Nat;

    function Sum is new Functional.Reduce_2012
      (Zero => 0,
       Binop => "+");

    package Num_IO is new Ada.Text_IO.Integer_IO (Num_Types.Nat);

    List : Num_Vecs.Vector;
begin
    for K in Num_Types.Nat range 1 .. 100 loop
       List.Append (K);
    end loop;
      
    Num_IO.Put (Sum (Splitting.Make_Iterator
       (First     => First (List),
       One_After => Next (To_Cursor (List,  50)))));

    Num_IO.Put (Sum ( Splitting.Make_Iterator
       (First     => To_Cursor (List, 51),
       One_After => Next (Last (List)))));
end Test_Iterations_2012;


Is Splitting written in the way we should, when using Ada 2012?



  parent reply	other threads:[~2012-05-30 22:25 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-05-29 14:29 Ada2012 : In praise of 'for ... of ... loop' Martin
2012-05-29 15:02 ` Georg Bauhaus
2012-05-30  8:10   ` Martin
2012-05-30  8:15     ` Thomas Løcke
2012-05-30 16:21       ` Pascal Obry
2012-05-30 19:30     ` Jeffrey Carter
2012-05-30 20:54       ` Pascal Obry
2012-05-31 14:09       ` Martin
2012-05-31 20:58         ` tonyg
2012-05-30 22:26     ` Georg Bauhaus [this message]
2012-05-30 22:45       ` Georg Bauhaus
2012-06-07  0:19       ` Randy Brukardt
2012-06-07 12:42         ` Georg Bauhaus
2012-06-07 12:54           ` Georg Bauhaus
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox