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=-1.9 required=5.0 tests=BAYES_00,LOTS_OF_MONEY autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,ce0900b60ca3f616 X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 2001-11-10 13:23:08 PST Path: archiver1.google.com!news1.google.com!newsfeed.stanford.edu!news-spur1.maxwell.syr.edu!news.maxwell.syr.edu!fu-berlin.de!uni-berlin.de!ppp-1-162.cvx2.telinco.NET!not-for-mail From: "Nick Roberts" Newsgroups: comp.lang.ada Subject: Re: List container strawman Date: Sat, 10 Nov 2001 20:59:53 -0000 Message-ID: <9sk5rn$140qdr$2@ID-25716.news.dfncis.de> References: <3BE29AF4.80804@telepath.com> <3BE29BD4.10401@telepath.com> <3BE2DB99.B707D409@boeing.com> <3BE32A18.18404AD1@boeing.com> <3BE443DE.574D669C@acm.org> <3BE58FDD.E1FB1815@san.rr.com> <3bec1cbe$0$15824$626a54ce@news.free.fr> <9sib27$13aeg3$5@ID-25716.news.dfncis.de> NNTP-Posting-Host: ppp-1-162.cvx2.telinco.net (212.1.140.162) X-Trace: fu-berlin.de 1005427385 37775803 212.1.140.162 (16 [25716]) X-Priority: 3 X-MSMail-Priority: Normal X-Newsreader: Microsoft Outlook Express 5.50.4133.2400 X-MimeOLE: Produced By Microsoft MimeOLE V5.50.4133.2400 Xref: archiver1.google.com comp.lang.ada:16246 Date: 2001-11-10T20:59:53+00:00 List-Id: "Ted Dennison" wrote in message news:L6dH7.20105$xS6.32596@www.newsranger.com... > In article <9sib27$13aeg3$5@ID-25716.news.dfncis.de>, Nick Roberts says... > >For a start, all this mularchy about insertion and deletion is just plain > >silly! It really is. You don't need them, and shouldn't implement them. Yes, > >really. Let me show you why. > > I'm not sure I understand this. It looks like you are talking about having the > users manage the internal details of all their own data structures, to which my > first reaction would be "ewwwww.". I'll admit there may be some interesting > possiblities I don't see in this though. In general, of course, you want to hide details such as pointers. But don't get carried away! Sometimes the application programmer has got to deal with things like pointers. In the example I gave, the fact that the list container was used to (explicitly) contain pointers is likely to actually have many advantages for the application programmer: other manipulations of the data - not involving containers - can also be carried out more efficiently (and sometimes more conveniently) using those same pointers. > >It also gives me another opportunity to demonstrate the idea of having a set > >of abstract container types, upon which operations (such as Normalize_Names) > >can be hung, thus freeing you of: (1) having to worry about which container > >type to use when writing the procedure; (2) the procedure shackling you to > >one particular container type. Do you turn away from this Utopia? ;-) > > But remember that one of our stipulations is essentially "no multilevel generic > instantiations required". Does this work that way? Without that restriction you > could do all sorts of cool things, as has been done in Booch and others. In that > enviroment I'd say that we already have plenty of players, and one of them is > liable to be perfectly suitable (although I suppose more are always welcome). I > don't want to discourage you from making the ultimate container library. But for > the purposes of what we are doing here it has to be very easy to use, and > frankly I couldn't figure out quite what was going on in the code you presented. I'm not quite trying to make 'the ultimate library' ;-) but I suppose I am aiming at something that would be useful for 'real' programming, rather than just for students and demos. Ada is not a toy language, it's not a Pascal or BASIC, it's an industrial language intended for building real, big software. Honestly, I think it's a mistake to try to defend students (or anyone else) from this fact. When we make a wooden box for the first time in kindergarten we use one nail per joint. As grown ups, when we make a box for some real purpose, we use two nails per joint. When making toy programs at university we may have used one instantiation per data type; but when as professionals we start writing real software, we use two instantiations per data type! (It's all a part of the maturing process ;-) At the end of the day, a box that falls to pieces (because you only used one nail) doesn't make life easier for anyone. As I say in my reply to Ehud, I really believe this is a situation where trying to make things easier for the user will end up making things much harder for them. To me, you really seem to be saying something like "Oh, there shouldn't be any safety catches on guns: it makes them too complicated for the user! Our guns have just the trigger and nothing else." It may sound nice, but you are inevitably going to cause of lot of users to end up shooting themselves (in the foot, or worse :-) I hope you'll forgive me if I re-post the code I gave, corrected and cleaned up a little, and with a blow-by-blow commentary on what's going on. with Ada.Characters.Handling; use Ada.Characters.Handling; This is to conveniently obtain some character-handling functions provided by Ada. type Name_Ref is access [all] String; Here we declare the access type that is going to be the 'data type' stored by containers. package Name_Ref_Iteration is new Iteration(Name_Ref); This instantiates the package of abstract container types (including Sequence_Recorder), so that they are specialised for Name_Refs (but they are still abstract). procedure Normalize_Names ( Source: in out Name_Ref_Iteration.Sequence_Recorder'Class; Target: in out Name_Ref_Iteration.Sequence_Recorder'Class) is Source and Target are both in the class of the abstract container type Sequence_Recorder. This means that the corresponding actuals, when the procedure is called, must be (concrete types) derived from Sequence_Recorder, and therefore that they must, at the very least, implement the procedures Read, Restart, Rewrite, and Write, and the function End_of_Data. Ref: Name_Ref; N: Natural; Just a couple of temporary variables. begin while not End_of_Data(Source) loop We will iterate over the elements (name pointers, of type Name_Ref) in the Source container. Read(Source,Ref); We read one reference in from the Source container. This is a dispatching call: the correct Read for the actual container will be called. if Ref /= null and then Ref.all'Length > 0 and then Ref(Ref.all'First) /= '?' then This just checks various conditions to ensure the name pointer (in Ref) is usable, and not to be deleted (indicated by the leading '?'). To_Upper(Ref.all); Convert the string (pointed to by the pointer in Ref) to uppercase. Write(Target,Ref); Write the pointer into the Target container. Again, this is a dispatching call. N := Ref.all.First-1; -- in case string doesn't start at 1 if Ref.all'Length >= 5 and then Ref(N+1..N+3) = "MAC" and then Is_Letter(Ref(N+4)) then Write(Target, new String'("MC"&Ref(N+4..Ref.all'Last))); end if; This is just a bit of messing about with the Macs. The important thing to note is that another Write may be performed, thus achieving the effect of insertion. else Ref := null; -- or maybe use Unchecked_Deallocation The effect of deletion is achieved by simply not writing the pointer into Target. end if; end loop; end Normalize_Names; The usage of this procedure, in conjunction with a (concrete) list container type (which we assume is derived from Sequence_Recorder), might be: package Name_Ref_Lists is new SCL.Lists.Unbounded(Name_Ref_Iteration); Now we instantiate the package that will export a list type for use (List_Type) wich is specialised for name pointers (type Name_Ref). subtype Name_List is Name_Ref_Lists.List_Type; This just conveniently renames the exported list type. L1, L2: Name_List; Here we declare two list variables. ... set up L1 with names We will do something (not shown, but e.g. read a file) to read the names, and put the pointers to them into L1. Rewrite(L2); This prepares (and erases, if necessary) L2 for being written into. Restart(L1); This prepares (and rewinds to the start, if necessary) L1 for being read from. Normalize_Names(L1,L2); We call the procedure. The actuals, L1 and L2, could have been of any container type (provided they were both derived from Sequence_Recorder). In this case, they happen to both be lists. Rewrite(L1); This is just a simple way to erase L1. Restart(L2); Get L2 ready to be read, for the next step in the overall processing (whatever that might be). I really hope this helps clarify things. The vital point is that the procedure Normalize_Names can be used with any container type (derived from Sequence_Recorder), rather than being permanently tied down to one container type. -- Best wishes, Nick Roberts