[comp.lang.modula2] Procedure Variables and Records and Sorting

George.Emery@p0.f6.n105.z1.fidonet.org (George Emery) (03/20/91)

Making use of procedure variables to set the sort method is old hat
to most folks.  What I'm trying to figure (can it even be done?) is
a way to allow a procedure to sort different types of records.  Say, for instance, you have several different types of records that happen to have integer values which are meaningful across all the records, but each record type has nothing else in common.  Is there a way to write a generic sort routine?  Or is this too far into object-oriented programming to not be Modula-2?
 
GE



--  
uucp: uunet!m2xenix!puddle!6.0!George.Emery
Internet: George.Emery@p0.f6.n105.z1.fidonet.org

warwick@cs.uq.oz.au (Warwick Allison) (03/21/91)

In <2848.27E71773@puddle.fidonet.org> George.Emery@p0.f6.n105.z1.fidonet.org (George Emery) writes:

>Making use of procedure variables to set the sort method is old hat
>to most folks.  What I'm trying to figure (can it even be done?) is
>a way to allow a procedure to sort different types of records.  Say, for
>instance, you have several different types of records that happen to
>have integer values which are meaningful across all the records, but
>each record type has nothing else in common.  Is there a way to write
>a generic sort routine?  Or is this too far into object-oriented
>programming to not be Modula-2?

Of course!  Anything is possible in Modula-2  :-)

Sort this:

VAR	ToBeSorted:ARRAY Index OF
		RECORD
			CASE RecordType:RecordTypeEnumeration OF
			  Type1:	r1:POINTER TO ActualType1;
			| Type2:	r2:POINTER TO ActualType2;
			| Type3:	r3:POINTER TO ActualType3;
			END;
		END;

Using the procedure:

PROCEDURE OutOfOrder(a,b:Index):BOOLEAN;

VAR	aKey:INTEGER;

BEGIN
	CASE ToBeSorted[a].RecordType OF
	  Type1:	aKey:=r1^.NumberOfToesField
	| Type2:	aKey:=r2^.NumberOfNosesField
	| Type3:	aKey:=r3^.NumberOfEyesField
	END;

	CASE ToBeSorted[a].RecordType OF
	  Type1:	RETURN aKey>r1^.NumberOfToesField
	| Type2:	RETURN aKey>r2^.NumberOfNosesField
	| Type3:	RETURN aKey>r3^.NumberOfEyesField
	END;
END OutOfOrder;


Sure, it's ugly, and the flexibility is low here, but it can be generalised.
The main problem really is the provision of arrays of heterogeneous records,
which is going to be a problem regardless of language!  Basically, you must
have a tag (it's even called that in M2) to determine which type of record
the variant (called that too) represents.

We are actually still sorting records of one type (that of ToBeSorted), but
we cannot have:

TYPE	Foobar=ARRAY [1..10] OF (x OR y OR MAYBE z);

so the problem never arises!  Sure, we may use untagged variant records, but
then the sort procedure would not know how to order the records.
--
  _--_|\   	warwick@cs.uq.oz.au
 /      *  <--	Computer Science Department,
 \_.--._/ 	University of Queensland,
       v    	AUSTRALIA.

lins@Apple.COM (Chuck Lins) (03/22/91)

In article <2848.27E71773@puddle.fidonet.org> George.Emery@p0.f6.n105.z1.fidonet.org (George Emery) writes:
>Making use of procedure variables to set the sort method is old hat
>to most folks.  What I'm trying to figure (can it even be done?) is
>a way to allow a procedure to sort different types of records.  Say, for instance, you have several different types of records that happen to have integer values which are meaningful across all the records, but each record type has nothing else in common.  Is there a way to write a generic sort routine?  Or is this too far into object-oriented programming to not be Modula-2?
> 
>GE

The sort routine should be given routines for swapping two elements given an
index as well as routines for comparison and perhaps copying. The procedures
are aware of the different arrays by the sort routine need not be. See 'The
Modula-2 Software Component Library' volume 4, the chapter on sorting.

-- 
Chuck Lins               | "Is this the kind of work you'd like to do?"
Apple Computer, Inc.     | -- Front 242
20525 Mariani Avenue     | Internet:  lins@apple.com
Mail Stop 37-BD          | AppleLink: LINS@applelink.apple.com
Cupertino, CA 95014      | "Self-proclaimed Object Oberon Evangelist"
The intersection of Apple's ideas and my ideas yields the empty set.

seurer+@rchland.ibm.com (Bill Seurer) (03/22/91)

Here's a minimal example that does what you want (I think).  I compiled
it and tried it with the test code and the end and it ran ok but I make
no guarantees!.  A different sorting method might require a slightly
different interface and different compilers might require different
sorts of type coercions.  Of course the interface to sort shown here
can't do much type checking since it uses the ADDRESS type.  A "real" OO
language could do better.
-=-=-=-=-
DEFINITION MODULE sort;
FROM SYSTEM IMPORT ADDRESS;
TYPE
  Result = (LessThan, Equal, GreaterThan);
  CompareProc = PROCEDURE (ADDRESS, ADDRESS): Result;
  SwapProc = PROCEDURE (ADDRESS, ADDRESS);
  NextProc = PROCEDURE (ADDRESS): ADDRESS;
PROCEDURE Sort (list: ADDRESS;
      Compare: CompareProc;
      Swap: SwapProc;
      Next: NextProc);
END sort.
-=-=-=-=-
IMPLEMENTATION MODULE sort;
FROM SYSTEM IMPORT ADDRESS;
PROCEDURE Sort (list: ADDRESS;
      Compare: CompareProc;
      Swap: SwapProc;
      Next: NextProc);
(* Bubble sort shown for example purposes only *)
(* No one would really use it (hopefully) *)
VAR
  this, that: ADDRESS;
BEGIN
  this := list;
  WHILE (this <> NIL) DO
    that := Next(this);
    WHILE (that <> NIL) DO
      IF Compare(this, that) = GreaterThan THEN
        Swap(this, that);
      END (*If*);
      that := Next(that);
    END (*While*);
    this := Next(this);
  END (*While*);
END Sort;
END sort.
-=-=-=-=-
MODULE trySort;
IMPORT InOut, sort;
FROM SYSTEM IMPORT ADDRESS;
FROM Storage IMPORT ALLOCATE;
TYPE
  ListPtr = POINTER TO ListRcd;
  ListRcd = RECORD
    val: CARDINAL;
    next: ListPtr;
  END (*List record*);
VAR
  list, first: ListPtr;
PROCEDURE Comp (a, b: ADDRESS): sort.Result;
 BEGIN
   IF ListPtr(a)^.val < ListPtr(b)^.val THEN
      RETURN sort.LessThan;
   ELSIF ListPtr(a)^.val > ListPtr(b)^.val THEN
      RETURN sort.GreaterThan;
   ELSE (*IF ListPtr(a)^.val = ListPtr(b)^.val THEN*)
      RETURN sort.Equal;
   END (*Else*);
 END Comp;
PROCEDURE Swap (a, b: ADDRESS);
 VAR
   tmp: CARDINAL;
 BEGIN
   tmp := ListPtr(a)^.val;
   ListPtr(a)^.val := ListPtr(b)^.val;
   ListPtr(b)^.val := tmp;
 END Swap;
PROCEDURE Next(a: ADDRESS): ADDRESS;
 BEGIN
    RETURN ListPtr(a)^.next;
 END Next;
BEGIN
  NEW(first);  list := first;  list^.val := 9;
  NEW(list^.next);  list := list^.next;  list^.val := 5;
  NEW(list^.next);  list := list^.next;  list^.val := 3;
  list^.next := NIL;
  sort.Sort(first, Comp, Swap, Next);
  list := first;
  WHILE list <> NIL DO
    InOut.WriteCard(list^.val, 2);
    list := list^.next;
  END (*While*);
  InOut.WriteLn;
END trySort.
-=-=-=-=-=-
Output:
 3 5 9

- Bill Seurer      IBM: seurer@rchland  Prodigy: CNSX71A
  Rochester, MN    Internet: seurer@rchland.vnet.ibm.com

Jon.Guthrie@p25.f506.n106.z1.fidonet.org (Jon Guthrie) (03/22/91)

 On a message of 19-Mar-91, George Emery (1:105/6.0) Said:

 > Making use of procedure variables to set the sort method is old hat to
 > most folks.  What I'm trying to figure (can it even be done?) is a way
 > to allow a procedure to sort different types of records.

In C, this sort of thing is a piece of cake because of the looser
typing.  Unfortunately, there's no way to define the type of an array
argument at runtime.  So, unless you're willing to put up with a
hideously ugly kludge, there's no way to write a general array sort in
Modula-2.  However...

It IS possible to write a generic linked-list sorting routine in M-2.
I, and others, have done so in C and I'm willing to convert the C code
that I have into M-2.

Of course, none of this does you the slightest bit of good if you
don't want to sort linked lists.  (You don't say what you want to
sort, but most people prefer to deal with arrays.)



--  
uucp: uunet!m2xenix!puddle!106!506.25!Jon.Guthrie
Internet: Jon.Guthrie@p25.f506.n106.z1.fidonet.org

George.Emery@p0.f6.n105.z1.fidonet.org (George Emery) (03/29/91)

Actually, I do prefer to use linked lists (some form of psychological limitation as a result of previous encounters with FORTH and LISP), particularly if I'm sorting large records.
 
GE



--  
uucp: uunet!m2xenix!puddle!6.0!George.Emery
Internet: George.Emery@p0.f6.n105.z1.fidonet.org

Jon.Guthrie@p25.f506.n106.z1.fidonet.org (Jon Guthrie) (04/03/91)

 On a message of 28-Mar-91, George Emery (1:105/6.0) Said:

 > Actually, I do prefer to use linked lists (some form of psychological
 > limitation as a result of previous encounters with FORTH and LISP),
 > particularly if I'm sorting large records.

Well, I tell you what.  I've got the StonyBrook compiler on order and
when it arrives, I'll recode the linked-list sort in Modula-2.  It
won't go out before this weekend (which is okay considering I'm so
busy at work I won't have a chance to get to it before then) but I'll
try and remember to post it.

One thing though:  I had completely forgotten about getting the
generic pointer (of type ADDRESS, that is) to an array and passing
THAT to the sorting routine.  If you do that, then you can write a
general sorting routine for arrays in Modula.  (I simply had never
thought of that before.)



--  
uucp: uunet!m2xenix!puddle!106!506.25!Jon.Guthrie
Internet: Jon.Guthrie@p25.f506.n106.z1.fidonet.org