From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=0.4 required=5.0 tests=BAYES_00,FORGED_MUA_MOZILLA, T_FILL_THIS_FORM_SHORT autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,51bff7cd4c35a15d X-Google-NewGroupId: yes X-Google-Attributes: gida07f3367d7,domainid0,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Received: by 10.68.238.65 with SMTP id vi1mr5200531pbc.7.1338416756904; Wed, 30 May 2012 15:25:56 -0700 (PDT) Date: Thu, 31 May 2012 00:26:02 +0200 From: Georg Bauhaus User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:12.0) Gecko/20120428 Thunderbird/12.0.1 MIME-Version: 1.0 Newsgroups: comp.lang.ada Subject: Re: Ada2012 : In praise of 'for ... of ... loop'... References: <74e4e6b5-20bd-4388-b4a0-dfbecc8070be@googlegroups.com> <4fc4e51d$0$6566$9b4e6d93@newsspool4.arcor-online.net> <371a4b67-1969-4cd5-90f4-d58a9b276f29@googlegroups.com> In-Reply-To: <371a4b67-1969-4cd5-90f4-d58a9b276f29@googlegroups.com> Message-ID: <4fc69e72$0$6633$9b4e6d93@newsspool2.arcor-online.net> Organization: Arcor NNTP-Posting-Date: 31 May 2012 00:25:55 CEST NNTP-Posting-Host: 51afe0d3.newsspool2.arcor-online.net X-Trace: DXC=a[EHSefTmVn_0Po7BmQ3]lA9EHlD;3Ycb4Fo<]lROoRa8kFjLh>_cHTX3jm[iH9a1He]>a X-Complaints-To: usenet-abuse@arcor.de Path: l9ni1429pbj.0!nntp.google.com!news1.google.com!goblin1!goblin2!goblin.stu.neva.ru!porbandar.httrack.net!news.httrack.net!feed.ac-versailles.fr!news.chainon-marquant.org!nntpfeed.proxad.net!proxad.net!feeder2-2.proxad.net!newsfeed.arcor.de!newsspool3.arcor-online.net!news.arcor.de.POSTED!not-for-mail Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Date: 2012-05-31T00:25:55+02:00 List-Id: 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?