Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!uwm.edu!zaphod.mps.ohio-state.edu!tut.cis.ohio-state.edu!ucbvax!ucsd!hub!eiffel!marc From: marc@eiffel.UUCP (Jean-Marc Nerson) Newsgroups: comp.lang.eiffel Subject: Re: Interesting problems to try in Eiffel Summary: Input consisting of a possibly large set of values Message-ID: <228@eiffel.UUCP> Date: 17 Jan 90 18:49:46 GMT References: <1721@novavax.UUCP> <631@enea.se> Organization: Interactive Software Engineering, Santa Barbara CA Lines: 349 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 Two solutions are suggested below. Solution 1 is the more complete of the two and makes full use of the object-oriented method: polymorphism, dynamic binding. Solution 2 is less advanced but will also work. It makes use of the persistence facilities of Eiffel. Its main drawback is that when you change input values you must change a class text and recompile a system. Although this system is very small and compilation will be quick, such an approach is subject to criticism. -------------------------------------------------------------- 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, the universal class ANY, and the Reverse Assignment Attempt (operator "?="). After such an instruction, there is no need here to test whether the RAA was successful since the programmer knows exactly what was entered in the hash table. 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. The solution achieves one of the major benefits of object-oriented design: limiting the number of explicit choices between a range of possibilities. There are explicit choices below, but this is inevitable since the basic classes must know the list of all possible types of input (string, real etc.); however there will be NO explicit choice in the application software. You do not need to change or recompile any of the basic classes when adding a new tag. Just record it with its type In practice the classes should be adapted to handle handling of and recovery from erroneous input. -------------------------------- deferred class INPUT export value, read feature value: ANY is deferred end; -- value read (f: FILE) is require not f.Void deferred end; -- read end -- clas INPUT -------------------------------- class REAL_INPUT export repeat INPUT inherit INPUT feature value: REAL; read (f: FILE) is require not f.Void do f.readreal; value := f.lastreal end; -- read end -- class REAL_INPUT -------------------------------- class STRING_INPUT export repeat INPUT inherit INPUT feature value: STRING; read (f: FILE) is require not f.Void do f.readword; value := f.laststring.duplicate end; -- read end -- class STRING_INPUT -------------------------------- class INT_INPUT export repeat INPUT inherit INPUT feature value: INTEGER; read (f: FILE) is require not f.Void do f.readint; value := f.lastint end; -- read end -- class INT_INPUT -------------------------------- class VALUE feature valve_count: INTEGER; pressure, temperature: REAL; active_valve: STRING; inputs: HTABLE [INPUT, STRING]; -- Internal Hash table init_inputs is -- Enter into `inputs' one instance of each -- possible type of VALUE. local pr: REAL_INPUT; pi: INT_INPUT; ps: STRING_INPUT; do inputs.Create (100); -- A value big enough to cover -- all possible types. pi.Create; inputs.put (pi, "valve_count"); pr.Create; inputs.put (pr, "pressure"); pr.Create; inputs.put (pr, "temperature"); ps.Create; inputs.put (ps, "active_valve"); 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 assign_inputs is -- Initialize `inputs' from values read in -- `.input' file and stored in hash table `inputs'. local pr: REAL_INPUT; pi: INT_INPUT; ps: STRING_INPUT do pi ?= inputs.item ("valve_count"); valve_count := pi.value; pr ?= inputs.item ("pressure"); pressure := pr.value; pr ?= inputs.item ("temperature"); temperature := pr.value; ps ?= inputs.item ("active_valve"); active_valve := ps.value end; -- assign_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; assign_inputs; debug io.putstring (out) end; -- Rest of the application -- ... end; -- Create -- ... end -- class APPLICATION -------- .input file ---- pressure 22.2 temperature 12.5 active_valve SOUTHEAST2 valve_count 78 -------------------------- SOLUTION 2 -=-=-=-=-= With this solution, you must adapt, compile and execute a very small system containing a class INPUT. The result of the execution is to store information into a file `.init' (using the automatic persistence facilities). The application system then reads the required values from file `.init'. -------------------------------- class INPUT export repeat STORABLE, pressure, valve_count, temperature, active_valve inherit STORABLE feature pressure, temperature: REAL; valve_count: INTEGER; active_valve: STRING end -- class INPUT -------------------------------- class INIT export repeat INPUT inherit INPUT feature Create is do pressure := 32.76; valve_count := 78; temperature := 12.5; active_valve := "SOUTHEAST2" -- ... Other possible value initializations here ... end; -- Create end -- class INIT -------------------------------- -- Root class of initialization system: -- class MAKE_INIT feature init: INIT; inputs: INPUT; Create is do init.Create; -- Creation of param instance necessary -- because we do not want to store an instance of INIT inputs.Create; -- Field by field copy. inputs.copy (init); inputs.store_by_name (".init") end; -- Create end -- class MAKE_INIT -------------------------------- -- 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 feature inputs: INPUT; -- Input values of the application Create is do -- Very beginning of the application inputs.Create; -- Get a pre-initialized object values from file '.init' inputs.retrieve_by_name (".init"); inputs := inputs.retrieved; -- Rest of the application, using -- inputs.pressure, inputs.valve_count, ... debug io.putstring (inputs.out) end; end; -- Create -- ... end -- class APPLICATION [All classes above have been compiled and tested with Eiffel 2.2.] -- Jean-Marc Nerson marc@eiffel.com