Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!cornell!uw-beaver!mit-eddie!mintaka!yale!cmcl2!esquire!yost From: yost@esquire.UUCP (David A. Yost) Newsgroups: comp.lang.eiffel Subject: Re: Interesting problems to try in Eiffel Message-ID: <1731@esquire.UUCP> Date: 18 Jan 90 21:00:25 GMT References: <1721@novavax.UUCP> <631@enea.se> <228@eiffel.UUCP> Reply-To: yost@esquire.UUCP (David A. Yost) Organization: DP&W, New York, NY Lines: 255 In article <228@eiffel.UUCP> marc@eiffel.UUCP (Jean-Marc Nerson) writes: > >A few posting recently (by Bob Weiner, Erland Sommarskog and Markku >Sakkinen) have raised the question of how to have input >(in the style of NAMELIST in Fortran) consisting of a possibly large >set of values, each characterized by a tag. > >Input values may be of various types. The tag uniquely determines the type. > >Sample input might look like the following, where each line contains >a pair: > > pressure 32.76 > valve_count 78 > temperature 12.5 > active_valve "SOUTHEAST 2" > >where the tag type information (entered separately) might be > > active_valve STRING > pressure REAL > temperature REAL > valve_count INTEGER > >Solution 1 is the more complete of [two given] and makes full use of >the object-oriented method: polymorphism, dynamic binding. > >-------------------------------------------------------------- >SOLUTION 1: >=-=-=-=-=-= > This solution is the complete implementation of what Erland Sommarskog > sketched out in <631@enea.se>: >(...) >>> A back-on-the-envelope solution to the problem would to be to have >>> class PARAMETER which inherits some sorting mechanism, probably a >>> hash table. Probably you have subclasses STRING_PARAMETER, >>> BOOLEAN_PARAMETER etc. You read the parameter string uses that as >>> key to get the instance, calls its Assign_value passing the value >>> string from the file. Assign_value parses the string and assigns >>> it to the Value feature or gives an error message. (...) > > The solution uses polymorphism, > To use dynamic binding, the solution relies on a hash table >which associates with each tag an object of the appropriate type. >These objects are shared (altogether, the table includes only one object >of each type). > > If no line beginning with a given tag appears in the input file, >the corresponding value will be set to the type's default. I didn't see where you did this in your example. > The solution achieves one of the major benefits of object-oriented >design: limiting the number of explicit choices between a range of >possibilities. Please explain. - - - - I liked a lot about your example, and I applaud the posting of actual practical code examples here. I noted in your code a couple of instances where basically the same code was repeated with minor changes (a red flag to me, which suggests a need for parameterization of some kind.) 1. The various forms of the INPUT classes 2. Having called the routine that read the input, you then had to call another routine to extract each value by hand from the hash table and copy it to the appropriate variable. I submit the following revised version, checked by the recently-posted eiffel_scanner, but not tried (I unfortunately don't currently have access to Eiffel compiler). My changes are marked by | in the first column. More remarks follow after the example. | -------------------------------- | | -- CHAR_SOURCE is a generic source from which characters can be read, | -- whether from a file, a string, or whatever. | | class CHAR_SOURCE export | getstring, getchar, putback | feature | | getstring (count: INTEGER): STRING is | -- get a string of characters | deferred | end; -- value | | getchar : STRING is | -- get a character | deferred | end; -- value | | putback (count: INTEGER): STRING is | -- put back a string of characters | -- onto the beginning of the stream | deferred | end; -- value | | end -- class CHAR_SOURCE | | -------------------------------- | | -- a class inheriting from this one is able to | -- consume characters from a CHAR_SOURCE and parse | -- them into a valid value for the object. | | class HAS_IN_FEATURE export | in | feature | | in (f: CHAR_SOURCE) is | -- get a string of characters from f | -- which represent a valid value for the object | -- and set the features of the object accordingly | deferred | end; -- value | | end -- class HAS_IN_FEATURE | | -------------------------------- | | deferred class INPUT [ T -> HAS_IN_FEATURE ] export | value, itsname, read | feature | | value: T; | itsname: STRING; | | Create (str: STRING, x: T, tbl: HTABLE [INPUT, STRING]) is | do | value := x; | itsname := str; | tbl.put (Current, str); | end; | | read (f: CHAR_SOURCE) is | require | not f.Void | do | value := T.in (f); | end; -- read | | end -- class INPUT | -------------------------------- class VALUE feature | in_valve_count : INPUT [ INT ]; | in_pressure : INPUT [ REAL ]; | in_temperature : INPUT [ REAL ]; | in_active_valve: INPUT [ STRING ]; | valve_count : INT is do Result := in_valve_count .value end; | pressure : REAL is do Result := in_pressure .value end; | temperature : REAL is do Result := in_temperature .value end; | active_valve: STRING is do Result := in_active_valve.value end; inputs: HTABLE [INPUT, STRING]; -- Internal Hash table init_inputs is -- Enter into `inputs' one instance of each -- possible type of VALUE. local do inputs.Create (100); -- A value big enough to cover -- all possible types. | in_valve_count .Create ("valve_count" , 0 , inputs); | in_pressure .Create ("pressure" , -1.0 , inputs); | in_temperature .Create ("temperature" , -1.0 , inputs); | in_active_valve.Create ("active_valve", "[none]", inputs); end; -- init_inputs read_inputs is -- Read inputs from input file `input' in -- current directory (no check). local file: FILE do from file.Create ("input"); file.open_read; file.readword until file.end_of_file loop inputs.item (file.laststring).read (file); file.readword end; file.close rescue io.putstring ("Input file `input' corrupted\n") end; -- read_inputs end -- class VALUE -------------------------------- -- Here is the way a class of the application system -- (for example the root class) will take care -- of reading the input values. class APPLICATION inherit VALUE feature Create is do | init_inputs; read_inputs; debug io.putstring (out) end; -- Rest of the application -- ... end; -- Create -- ... end -- class APPLICATION - - - - - Notes: 1. The C library has fprintf and sprintf which do the same output formatting to either a file or a string, a rudimentary kind of polymorphism. My CHAR_SOURCE class proposed here is the input equivalent of that, rendered in an OO context. That is, there is a class of object that can supply characters. This could be a file or a string or some other data structure. There should also be a CHAR_DESTINATION (or some other name) class to serve the same purpose on output. These would greatly facilitate writing reusable code that does I/O. This approach would lead to some rethinking of the I/O in the Eiffel library. 2. I don't know offhand how you would implement the 'in' feature of class HAS_IN_FEATURE class in my example. 3. There are still two places in my version where basically the same code with a name change is repeated four times. Oh, well. --dave