Comparing Object-oriented Languages

Comparing one language to another usually is like comparing coconuts to kumquats. To make comparisons easier, we implemented a double-ended linked-list class in C++, then in Smalltalk, Eiffel, Sather, Objective-C, Parasol, Beta, Turbo Pascal, C+@, Liana, Ada, and, yes, even Drool.


October 01, 1993
URL:http://www.drdobbs.com/cpp/comparing-object-oriented-languages/184409096

OCT93: Comparing Object-oriented Languages

Mike is executive editor for DDJ and can be contacted through the DDJ offices or on CompuServe at 76703,4057.


While many programmers proclaim C++ to be the object-oriented language of the '90s, others find the quest for the perfect language to be elusive. In fact, deciding on the right language can be downright confusing. "Pure" OO languages like Smalltalk include a rich set of reusable classes, while hybrids such as C++ force you to roll your own. Languages such as Eiffel promote (or impose) their notion of software correctness, which is supported by the syntax of the language. Some languages are compiled, others interpreted, and a couple even provide both. Additionally, many languages are adding support for concurrency and multithreading. And while most languages are general purpose, others like Drool are context-specific. In short, there's an endless choice of options and the right language largely depends upon your specific needs.

To aid in your search, DDJ invited a number of programmers to implement a simple linked-list class in their favorite language. The linked-list was chosen because it's widely used and understood, making it easier to compare how different languages, approach a given problem. This, in effect, allows the code to speak for itself. To this end, you can examine and compare approaches in C++. Smalltalk, Eiffel, Sather, Objective-C, Parasol, Beta, Turbo Pascal, C+@, Liana, Ada, and, yes, even Drool.

Specifications and Requirements

Because most traditional compiled languages bind data elements at compile time, linked-lists are usually homogenous; that is, a given list can only store elements of a single data type. Consequently, programmers are constantly rewriting linked-list code to support new and different data types, and applications must duplicate code that manages these lists. Object-oriented languages provide run-time binding (often called "late" or "dynamic binding") which allows the binding of data elements to be deferred. This feature, which allows the creation of a heterogeneous list, lets you store different object types.

We asked our participants to create a double-ended linked-list class that's capable of handling multiple object types. The class needed to include methods for creating a new list, adding a list element to the end of the list, and printing individual list elements. Of course, a full-featured linked-list class would require methods to get the next and previous elements, get the head and tail of the list, append, delete, count elements in the list, check for an empty list, and iterators to iterate over the elements in the list. DDJ contributing editor David Betz supplied the C++ version (see Listing One, page 118), which served as an admittedly less-than-scientific specification. We asked our various programmers to duplicate the functionality of Listing One, although not necessarily the program structure or even variable names. This allowed them to approach the linked-list problem in the manner natural to each language.

Since linked-lists are well-known and widely used dynamic data structures, I won't belabor Listing One's code; however, I will point out, that Listing One declares two object types, MyNumber and MyPoint. main() constructs instances of both objects and stores them in their respective lists--numbers are stored in list1 and points are placed in list2. To demonstrate that the lists are heterogeneous, main() also places points in list1 numbers in list2, and finishes off by storing list1 in list2. Finally, the lists are printed and the program terminates.

What to Look For

It was up to the individual programmer to implement the list-manager code which, of course, depends on the facilities available in a particular language. For example, Smalltalk includes an OrderedCollection class that provides most of the functionality required for this project. Therefore, the Smalltalk version in Listing Seven, page 122, merely subclasses from class OrderedCollection, and later creates a list instance, instantiates the objects, and adds them to the list. In most languages, however, containers are not a standard part of the language. This is reflected in the number of lines of code for each language.

Table 1 illustrates this point by summarizing the number of lines of code each language used in the linked-list project. It's important to keep in mind that the line count is a deceiving number since it doesn't reflect the underlying mechanisms being executed. (Not to mention that in most cases blank lines have been deleted simply to conserve space.) Facilities such as a garbage collector, exception handlers, built-in support for collections and containers (as noted above), and inheritance mechanisms all affect the line count. In the case of automatic garbage collection, for example, the compiler allocates and manages memory, which frees the programmer from worries such as orphaned pointers, memory leaks, and the like. It also results in slightly less source code since constructors (if any) are reduced to initializers and destructors go away.

Exception handlers beef up the source code a little, but the gain is worth the pain. Exceptions provide a way of dealing with a failed execution of an instruction or routine. Without an exception handler, the failure may cause the system to terminate unpredictably. In Table 1, note that, while exceptions are provided for in Stroustrup's The annotated C++ Reference Manual (often referred to as the ARM), they're still listed as "experimental" and many C++ vendors have yet to implement them.

The most dramatic affect on line count, however, is the syntax of the language. For example, the C++ version makes use of initializers in class declarations: MyListElement() includes {data = initialData;} all on a single line within the declaration. However, the Turbo Pascal version I wrote (Listing Three, page 119) initializes data within its MyListElement Init constructor. The constructor requires a minimum of four lines of code. (Note that source code compactness does not translate into fewer instructions.) I could site other example, but you get the idea.

Programming Notes

Implementing the linked-list code in Smalltalk can be done in either of two ways. The first approach is to conduct a straightforward translation of the C++ version of Smalltalk. However, an experienced Smalltalk programmer might choose an alternative approach, which is to do the minimum necessary to sat can be done in four lines of code. The test program can be satisfied by a protocol consisting of three methods: Create a list, add an element to a list, and (recursively) print the elements of a list. Most of this behavior is already available in class Ordered-Collection, one of the standard classes in Smalltalk/V. Listing Seven, page 122, which was provided by DDJ senior technical editor Ray Valdes, shows how to subclass OrderedCollection and implement the modicum of additional behavior (the printOn: method). Of course, in a real-world setting, a link-list class would require a richer protocol. This would require additional code, as would be the case in the other languages, and would probably require building on top of the from-scratch version shown in Listing Eight, page 122, rather than subclassing OrderedCollection.

Also, notice in Table 1, that we've included Ada in the linked-list project. While not object oriented, Ada is still considered to be object based. Because Ada currently doesn't support run-time binding, List_Example in Listing Thirteen (page 124) can only handle homogenous lists. According to Mike Ruf at Rational Systems, there are ways to simulate run-time polymorphism in Ada83 (see comments in Listing Thirteen). It's also worth noting that the next Ada standard is expected to support single inheritance and run-time polymorphism.

Conclusion

As you browse the listings you'll find Robert Jervis's Parasol implementation (Listing Four, page 120), Jim Fleming's C+@ implementation (Listing Five, page 120), a Liana implementation by Jack Krupansky of Base Technology (Listing Six, page 121), an Eiffel implementation by Robert Howard of Tower Technology (Listing Nine, page 122), a Sather implementation by Stephen Omohundro of ICSI (Listing Ten, page 124), a Beta implementation from Steve Mann of MADA (Listing Eleven, page 124), and David Betz's Drool (Listing Twelve, page 124). You'll also find more coverage on each of the these languages elsewhere in this issue. In addition to the C++, Turbo Pascal, Smalltalk, and Ada listings previously mentioned in this article, Stephen Asbury of Next Computer presents an Objective-C Implementation (Listing Two, page 118).

Finally, the linked-list project is not a contest to find the best object-oriented language; rather, it's a window into the soul of each language. And while the number of lines of code may not provide a realistic comparison between languages, it is how many managers gauge programmer productivity. This, in fact, may be the poorest of measures, especially when you factor in code reuse.

Table 1:


             Garbage                  Late               Exception   Lines of
             Collection  Inheritance  Binding  Compiled  Handling    Code (approx.)
C++          No          Multiple     Yes      C         Yes          85
Objective-C  No          Single       Yes      C         No          120
Turbo Pascal No          Single       Yes      C         No          140
Parasol      No          Single       Yes      C         No           75
C+@          Yes         Multiple     Yes      C         No          110
Liana        Yes         Single       Yes      I         No           33
Smalltalk    Yes         Single       Yes      I/C       No          *44
Eiffel       Yes         Multiple     Yes      **I/C     Yes         155
Sather       Yes         Multiple     Yes      I/C       Yes          80
Beta         Yes         Single       Yes      C         Yes          55
Drool        Yes         Multiple     Yes      Byte Code No           45
Ada          No          ***None      ***No    C         Yes         125

Key object-oriented features. 1=interpreted. C=compiled.
*Line count given is for the minimal implementation; full implementation is 145 lines of code. 
**Eiffel uses its "melting ICE" technology to interpret small chunks of code for testing purposes. 
***The next Ada standard is expected to support both single inheritance and run-time binding.



_COMPARING OBJECT-ORIENTED LANGUAGES_
by Michael Floyd


[LISTING ONE]


C++ Implementation by David Betz (Dr. Dobb's Journal)


#include <iostream.h>

/* any object that will appear in a list must inherit from this class */
class MyListData {
public:
    virtual void Print(void);
};
void MyListData::Print(void)
{
    cout << "No print method!\n";
}
class MyListElement {
     MyListData *data;
     MyListElement *next;
public:
     MyListElement(MyListData *initialData) { data = initialData; next = nil; }
     friend class MyList;
};
class MyList : public MyListData {
    MyListElement *head;
    MyListElement *tail;
public:
    MyList(void) { head = tail = nil; }
    void AddToList(MyListData *data);
    void Print(void);
};
void MyList::AddToList(MyListData *data)
{
    MyListElement *newElement = new MyListElement(data);
    if (head == nil)
        head = tail = newElement;
    else {
        tail->next = newElement;
        tail = newElement;
    }
}
void MyList::Print(void)
{
    MyListElement *element;
    for (element = head; element != nil; element = element->next)
    element->data->Print();
}
class MyNumber : public MyListData {
    int value;
public:
    MyNumber(int initialValue) { value = initialValue; }
    virtual void Print(void);
};
void MyNumber::Print(void)
{
    cout << "Number: " << value << "\n";
}
class MyPoint : public MyListData {
    int x;
    int y;
public:
    MyPoint(int initialX,int initialY) { x = initialX; y = initialY; }
    virtual void Print(void);
};
void MyPoint::Print(void)
{
    cout << "Point: " << x << "," << y << "\n";
}
void main(void)
{
    MyList *list1 = new MyList;
    MyList *list2 = new MyList;
    MyNumber *n1 = new MyNumber(10);
    MyNumber *n2 = new MyNumber(20);
    MyPoint *p1 = new MyPoint(2,3);
    MyPoint *p2 = new MyPoint(4,5);
    /* build the lists */
    list1->AddToList(n1);
    list1->AddToList(n2);
    list1->AddToList(p1);
    list2->AddToList(n2);    /* an object can be in more than one list    */
    list2->AddToList(p1);    /*    at the same time                       */
    list2->AddToList(p2);
    list2->AddToList(list1); /* we can even put a list into another list */
    /* print the lists */
    cout << "list1:\n";
    list1->Print();
    cout << "list2:\n";
    list2->Print();
}


[LISTING TWO]


Objective-C Implementation by Stephen Asbury (Next Computer)

#import <stdio.h>
#import <objc/Object.h>/* Include the header for the Root class */

@interface LinkedListNode:Object /* declare a node class */
{
    id value;/* a node can store an object of any class as it's
value */
    id next;
}
- setValue:newValue;
- value;
- setNext:newNext;
- next;
- print;
@end
@implementation LinkedListNode /* implement the node class */
- setValue:newValue {value = newValue; return self;}
- value {return value;}
- setNext:newNext {next = newNext; return self;}
- next {return next;}
- print
{   /* In order to allow any object to be added to the list, we first check
    * to see if the value can print, if so have it print */
    if([value respondsTo:@selector(print)] == YES) [value print];
    return self;
}
@end
@interface LinkedList:Object /* declare a linked list class */
{id tail,head;}
- addObjectToList:theObject;
- print;
@end
@implementation LinkedList /* implement the linked list class */
- init /* refine the init method of the super class */
{
    self = [super init];
    tail = head = nil;
    return self;
}
- addNodeToList:newNode /* private method used only by the object */
{
    if(head == nil)
        head = tail = newNode;
    else{
        [tail setNext:newNode];
        tail = newNode;
    }
    return self;
}
- addObjectToList:theObject
{
    id newNode = [[[LinkedListNode alloc] init]
setValue:theObject];
    [self addNodeToList:newNode];
    return self;
}
- print
{
    id element;
    for(element = head;element != nil;element = [element next])
        [element print];
    return self;
}
@end
@interface MyNumber:Object /* declare a number class */
{float value;}
- initValue:(float)aValue;
- print;
@end
@implementation MyNumber /* implement the number class */
- initValue:(float)aValue
{
    self = [super init];
    value = aValue;
    return self;
}
- print {printf("Number %f\n",value); return self;}
@end

@interface Point:Object /* declare a point class */
{float X,Y;}
- initX:(float)anX y:(float)anY;
- print;
@end
@implementation Point /* implement the point class */
- initX:(float)anX y:(float)anY
{
    self = [super init];
    X = anX;
    Y = anY;
    return self;
}
- print { printf("Point %f , %f\n",X,Y); return self; }
@end
void main(void)
{
    id list1 = [[LinkedList alloc] init];
    id list2 = [[LinkedList alloc] init];
    id number1 = [[MyNumber alloc] initValue:10];
    id number2 = [[MyNumber alloc] initValue:20];
    id point1 = [[Point alloc] initX:2 y:3];
    id point2 = [[Point alloc] initX:4 y:5];
    /* Build the Lists */
    [list1 addObjectToList:number1];
    [list1 addObjectToList:number2];
    [list1 addObjectToList:point1];
    [list2 addObjectToList:number2];/* an object can be in
multiple list */
    [list2 addObjectToList:point1];
    [list2 addObjectToList:point2];
    [list2 addObjectToList:list1];/* lists can contatin lists */
    printf("List 1\n");
    [list1 print];
    printf("List 2\n");
    [list2 print];
    exit(0);
}

[LISTING THREE]


Turbo Pascal Implementation by Michael Floyd (Dr. Dobb's Journal)

Program LListObj;

Type
  PMyListData = yListData;
  MyListData = object
     procedure Print; virtual;
  end;
  PMyListElement = yListElement;
  MyListElement = object
    Data : PMyListData;
    Next : PMyListElement;
    constructor Init(initialData: PMyListData);
  end;
  PMyList = yList;
  MyList = object(MyListData)
    Head, Tail: PMyListElement;
    constructor Init;
    procedure AddToList(var Data: PMyListData);
    procedure Print; virtual;
  end;
   PMyNumber = yNumber;
   MyNumber = object(MyListData)
      Value : Integer;
      constructor Init(initialValue: Integer);
      procedure Print; virtual;
   end;
   PMyPoint = yPoint;
   MyPoint = object(MyListData)
      X, Y: Integer;
      constructor Init(initialX, initialY: Integer);
      procedure Print; virtual;
   end;
{ MyListElement Methods }
constructor MyListElement.Init(initialData: PMyListData);
begin
   data := initialData;
   next := nil;
end;
{ MyListData Methods }
procedure MyListData.Print;
begin
   writeln('No Print Method!');
end;
{ MyList Methods }
constructor MyList.Init;
begin
   Head:= nil;
   Tail:= nil;
end;
procedure MyList.AddToList(var Data: PMyListData);
var
  Added : PMyListElement;
begin
  Added := New(PMyListElement,Init(Data));
  If Head = nil then
  begin
    Head := Added;
    Tail := Added;
  end
  Else begin
    Tail^.Next := Added;
    Tail := Added;
  end;
  Added^.Next := nil;
end;
procedure MyList.Print;
var
   Current : PMyListElement;
begin
  Current := Head;
  while Current <> nil do
  begin
      Current^.Data^.Print;
      Current := Current^.Next;
  end;
end;
{ MyNumber Methods }
constructor MyNumber.Init(initialValue: Integer);
begin
   Value := initialValue;
end;
procedure MyNumber.Print;
begin
   writeln('Number: ', Value);
end;
{ MyPoint Methods }
constructor MyPoint.Init(initialX, initialY: Integer);
begin
   X := initialX;
   Y := initialY;
end;
procedure MyPoint.Print;
begin
   writeln('Point: ', X, ',',Y);
end;
{ Main }
Var
   NumList, PointList : MyList;
   Number, Coordinates: PMyListData;
   I, J               : Integer;
Begin
   with NumList do begin      { create list of numbers }
      Init;
      For I := 1 to 5 do
      begin
        Number := New(PMyNumber, Init(I));
        AddToList(Number);
      end;
      Print;
   end;
   with PointList do begin    { create list of points }
      Init;
      For I := 1 to 5 do
      begin
        J := I + 1;
        Coordinates := New(PMyPoint, Init(I,J));
        AddToList(Coordinates);
      end;
      Print;
   end;
   NumList.AddToList(Coordinates);    { Coords can be added to Number list }
   NumList.Print;
   readln;
end.

[LISTING FOUR]


Parasol Implementation by Robert Jervis


include file;
/* any object that will appear in a list must inherit from this class */
MyListData: type    { public:
Print:  dynamic () = {
    printf("No print method!\n");
    }
};
MyList: public  type inherit MyListData {
    head:   ref MyListElement;
    tail:   ref MyListElement;
    MyListElement:  type {
    public:
        data:   ref MyListData;
        next:   ref MyListElement;
    constructor:    (initialData: ref MyListData) =
                { data = initialData; next = 0; }
    };
public:
constructor:    () = { head = tail = 0; }

AddToList:  (data: ref MyListData) = {
    newElement: ref MyListElement = new MyListElement[data];
        if  (head == 0)
                head = tail = newElement;
        else    {
                tail->next = newElement;
                tail = newElement;
        }
    }
Print:  dynamic () = {
    element:    ref MyListElement;
    for (element = head; element != 0; element = element->next)
        element->data Print();
    }
};
MyNumber:   type    inherit MyListData {
    value:  int;
public:
constructor:    (initialValue: int)  = { value = initialValue; }
Print:  dynamic () = {
    printf("Number: %d\n", value);
    }
};
MyPoint:    type    inherit MyListData {
    x:  int;
    y:  int;
public:
constructor:   (initialX: int, initialY: int) = { x = initialX; y = initialY; }
Print:  dynamic () = {
    printf("Point: %d,%d\n", x, y);
    }
};
main:   entry   () =
{
    list1:  ref MyList = new MyList[];
    list2:  ref MyList = new MyList[];
    n1: ref MyNumber = new MyNumber[10];
    n2: ref MyNumber = new MyNumber[20];
    p1: ref MyPoint = new MyPoint[2,3];
    p2: ref MyPoint = new MyPoint[4,5];
        /* build the lists */
    list1 AddToList(n1);
    list1 AddToList(n2);
    list1 AddToList(p1);
    list2 AddToList(n2);    /* an object can be in more than one list */
    list2 AddToList(p1);    /*    at the same time                    */
    list2 AddToList(p2);
    list2 AddToList(list1); /* we can even put a list into another list */
        /* print the lists */
    printf("list1:\n");
    list1 Print();
    printf("list2:\n");
    list2 Print();
}

[LISTING FIVE]


C+@ Implementation by Jim Fleming (Unir Corp.)


class MyListData {
   method print
   {
       "No print method".print;
   }
}
class MyListElement {
   inherit MyListData;
   MyListData    data;
   MyListElement next;

   class method (_) new (initialData) {
           _ = create
           _.init(initialData);
   }
   method init (idata)  {
       data = idata;
       next = nil;
   }
   method (_) data {
       _ = data;
   }
   method(_) next {
       _ = next;
   }
   method linkTo (listElement) {
       next = listElement;
   }
}
class MyList {
   MyListElement head;
   MyListElement tail;

   class method (_) new {
      _ = create;
      _.init;
   }
   method init {
      head = nil;
      tail = nil;
   }
   method addToList (data) {
      var newElement;
           newElement = MyListElement.new(data);
           if(head == nil) {
              head = newElement;
              tail = head;
           }
           else{
              tail.linkTo(newElement);
              tail = newELement;
           }
     }
     method print {
        var element;
        for (element = head; element != nil; element = element.next) {
           element.data.print;
        }
     }
}
class MyNumber {
   Integer value;
   class method (_) new (initialValue) {
      _= create;
       .init (initialValue)
    }
    method init (initialValue) {
        value = initialValue;
    }
    method print  {
       ("Number: " // value // "\n").print;
    }
}
class MyPoint  {
   Integer x;
   Integer y;
   class method (_) new (initialX,initialY) {
      _= create;
      .init(initialX,initialY);
   }
   method init (initialX,initialY) {
      x = initialX;
      y = initialY;
    }
    method print {
      ("Point: "// x // "," // y // "\n").print
    }
}
/* The following can be typed directly into command shell */
list1 = Mylist.new;
list2 = Mylist.new;
n1 = Mynumber.new(10);
n2 = Mynumber.new(20);
p1 = MyPoint.new(2,3);
p2 = MyPoint.new(4,5);

list1.addToList(n1);
list1.addToList(n2);
list1.addToList(p1);
list2.addToList(n2); /* an object can be in more than one list */
list2.addToList(p1);
list2.addToList(p2);
list2.addToList(list1); /* we can even put a list into another list */
/* print the lists */
"list1;\n".print;
list1.print;
"list2;\n".print;
list2.print;


[LISTING SIX]


Liana Implementation by Jack Krupansky (Base Technology)

class  MyList : array
{
           Print
           {
                  for (int i = 0, int n = size; i < n; i++)
                       if ((any e = this [i]).isa ("MyList"))
                                e.Print();
                       else
                                cout << e.class_name+": "+e.text+"\n";
           }
};
//-------------------------------------------
void main (void)
{
        MyList  list1 =   new   MyList;
        MyList  list2 =   new   MyList;
        int     n1 =      10;
        int     n2 =      20;
        point   p1 =      new   point (2,3);
        point   p2 =      new   point (4,5);

        /* build the lists */
        list1 << n1 << n2 << p1;

        /* an obj can be in more than one lst at same time */
        list2 << n2 << p1 << p2;

        list2 << list1; /* we can even put a list into another list  */

        /* print the lists */
        cout << "\nLIST1:\n";    list1.Print;
        cout << "\nLIST2:\n";    list2.Print;
}

[LISTING SEVEN]


Minimal Smalltalk implementation by Ray Valdes (Dr. Dobb's Journal)

OrderedCollection subclass: #MyListClass
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''   !
!MyListClass class methods ! !
!MyListClass methods !
printOn: aStream
    " Method to display list elements, recursing if necessary "
    aStream cr; nextPutAll:  ' ',(self class name),' '; cr.
    self do: [:c | c printOn: aStream. aStream cr ].
    ^self.
!
test
    " Program to test the linked-list class "
    |
       aList1 aList2 aNumber1 aNumber2 aPoint1 aPoint2 aMsg
    |
    aList1 := MyListClass new.
    aList2 := MyListClass new.
    aNumber1 := 10.
    aNumber2 := 20.
    aPoint1 := (2 @ 3).
    aPoint2 := (4 @ 5).
    " build the lists "
    aList1 add: aNumber1; add: aNumber2; add: aPoint1.
    " an object can be in more than one list at same time "
    aList2 add: aNumber2; add: aPoint1;      add: aPoint2.
    " we can even put a list into another list "
    aList2 add: aList1.
    " print the lists "
    aList1 printOn: Transcript.
    aList2 printOn: Transcript.
! !


[LISTING EIGHT]


Complete Smalltalk/V implementation by Ray Valdes (Dr. Dobb's Journal)

"-------------------------------------------------------------"
Object subclass: #MyListClasses
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''    !
!MyListClasses class methods ! !
!MyListClasses methods ! !
"-------------------------------------------------------------"
MyListClasses subclass: #TestList
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''    !
!TestList class methods ! !
!TestList methods !
test
    " Program to test the linked-list class "
    |
       aList1 aList2 aNumber1 aNumber2 aPoint1 aPoint2
    |
    aList1   := MyList new.
    aList2   := MyList new.
    aNumber1 := MyNumber new: 10.
    aNumber2 := MyNumber new: 20.
    aPoint1  := MyPoint  new: (2 @ 3).
    aPoint2  := MyPoint  new: (4 @ 5).
    " build the lists "
    aList1 add: aNumber1; add: aNumber2; add: aPoint1.
    " an object can be in more than one list at same time "
    aList2 add: aNumber2; add: aPoint1;      add: aPoint2.
    " we can even put a list into another list "
    aList2 add: aList1.
    " print the lists "
    aList1 print.
    aList2 print! !
"------------------------------------------------------------------"
MyListClasses subclass: #MyListElement
  instanceVariableNames: 'data next'
  classVariableNames: ''
  poolDictionaries: ''    !
!MyListElement class methods !
new: someData
   ^super new initialize: someData! !
!MyListElement methods !
initialize: someData
     data := someData.
     ^self!
next
    ^next!
next: anElement
      next := anElement.
      ^self!
print
      data print.
      ^self! !
"-------------------------------------------------------------"
MyListClasses subclass: #MyListData
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''    !
!MyListData class methods ! !
!MyListData methods !
print
   Transcript cr; nextPutAll: 'Must override this method'; cr.
   ^self! !
"-------------------------------------------------------------"
MyListData subclass: #MyList
  instanceVariableNames: 'head tail'
  classVariableNames: ''
  poolDictionaries: ''  !
!MyList class methods ! !
!MyList methods !
add: someData
    | anElement |
    anElement := MyListElement new: someData.
    head isNil
      ifTrue:  [ head := anElement. tail := anElement ]
      ifFalse: [ tail next: anElement.  tail := anElement].
     ^self!
print
    | anElement |
    anElement := head.
    [anElement isNil]
    whileFalse: [
        anElement print.
        anElement := anElement next .
        Transcript cr.
        ].
     ^self! !
"-------------------------------------------------------------"
MyListData subclass: #MyNumber
  instanceVariableNames: 'value'
  classVariableNames: ''
  poolDictionaries: ''    !
!MyNumber class methods !
new: aValue
    ^super new initialize: aValue! !
!MyNumber methods !
initialize: aValue
     value := aValue.
     ^self!
print
    value printOn: Transcript.
    ^self! !
"-------------------------------------------------------------"
MyListData subclass: #MyPoint
  instanceVariableNames: 'point'
  classVariableNames: ''
  poolDictionaries: '' !
!MyPoint class methods !
new: aPoint
    ^super new initialize: aPoint! !
!MyPoint methods !
initialize: aPoint
    point := aPoint.
    ^self!
print
    point printOn: Transcript.
    ^self! !


[LISTING NINE]


Eiffel Implementation by Robert Howard (Tower Technology)


class DRIVER
   -- In Eiffel, the top level driver is an object, too.
inherit
   BASIC_IO
creation
   make
feature {ANY}
   make is
     -- run this test driver
      local
     list1, list2 : MY_LIST[PRINTABLE] ;
     n1, n2 : MY_NUMBER ; -- a kind of PRINTABLE
     p1, p2 : MY_POINT ;  -- also a kinf of PRINTABLE
      do
         -- create the various objects
     !!list1 ;
     !!list2 ;
     !!n1.set( 10 ) ;
     !!n2.set( 20 ) ;
     !!p1.set( 2, 3 ) ;
     !!p2.set( 4, 5 ) ;

     list1.add_to_list( n1 ) ;
     list1.add_to_list( n2 ) ;
     list1.add_to_list( p1 ) ;
     list2.add_to_list( n2 ) ; -- objects can be in more than one list
     list2.add_to_list( p1 ) ;
     list2.add_to_list( p2 ) ;
     list2.add_to_list( list1 ) ; -- list 1 is an element of list 2

     put_string( "list1:%N" ) ;
     list1.print_self ;
     put_string( "list2:%N" ) ;
     list2.print_self ;
      end -- make
end -- DRIVER
deferred class PRINTABLE
   -- insures that 'print_self' is implemented
inherit
   BASIC_IO
feature {ANY}
   print_self is
     -- print yourself
      deferred
      end -- print_self
end -- PRINTABLE
class MY_NUMBER
   -- holds and can print an integer
inherit
   PRINTABLE
creation
   set
feature {ANY}

   value : INTEGER ;
   set( new_value : INTEGER ) is
     -- set this number
      do
     value := new_value ;
      end ; -- make
   print_self is
     -- print the value
      do
     put_string( "Number: " ) ;
     put_int( value ) ;
     put_newline ;
      end ; -- print_self
end -- MY_NUMBER
class MY_POINT
   -- holds and can print an x,y pair
inherit
   PRINTABLE
creation
   set
feature {ANY}
   x, y : INTEGER ;
   set( new_x : INTEGER; new_y : INTEGER ) is
     -- set this point
      do
     x := new_x ;
     y := new_y
      end ; -- set
   print_self is
     -- print the value
      do
     put_string( "Point: " ) ;
     put_int( x ) ;
     put_char( ',' ) ;
     put_int( y ) ;
     put_newline ;
      end ; -- print_self
end -- MY_POINT
class ELEMENT[T]
   -- holds an object reference and a
   -- single link to another ELEMENT[T]
creation
   set_data
feature {LIST}
   data : T ;
   next : ELEMENT[T] ;
   set_data( new_data : T ) is
     -- set data to the new_data
      do
     data := new_data
      end ; -- set_data
   set_next( new_next : ELEMENT[T] ) is
     -- set next to the element
      do
     next := new_next

      end ; -- set_next
end -- class ELEMENT
class LIST[T]
   -- a generic linked list class
feature {ANY}
   add_to_list( data : T ) is
     -- add to the end of the list
      local
     new_element : ELEMENT[T] ;
      do
     !!new_element.set_data( data ) ;
     if head = void
      then
        head := new_element ;
     else
        tail.set_next( new_element ) ;
     end
     tail := new_element ;
      end ; -- add_to_list
feature {NONE}
   head, tail : ELEMENT[T] ;
invariant
   tail_next_is_void: tail.next = void ;
   tail_void_when_head_void: head = void implies tail = void
end -- LIST
class MY_LIST[T->PRINTABLE]
   -- A printable list which holds printable data
inherit
   LIST[T]
   PRINTABLE
feature {ANY}
   print_self is
     -- print the list elements
      local
     el : ELEMENT[T] ;
      do
     from
        el := head
     until
        el = void
     loop
        el.data.print_self
        el := el.next ;
     end ;
      end ; -- print_self
end -- MY_LIST


[LISTING TEN]


Sather Implementation by Stephen Omhundro (ICSI)

-------------------------------------------------------------------
abstract class $MY_LIST_DATA is
   -- Data that will appear in a list must inherit this class.

   print is
      -- Print the data on OUT. Should be redefined in descendants.
      #OUT + "No print routine defined!\n" end;
end;
-------------------------------------------------------------------
class MY_LIST_ELEMENT is
   -- An element of a MY_LIST.

   attr data:$MY_LIST_DATA;
   attr next:MY_LIST_ELEMENT;
end;
-------------------------------------------------------------------
class MY_LIST is
   -- A double ended linked list.
   inherit $MY_LIST_DATA;

   private head, tail:MY_LIST_ELEMENT;

   add_to_list($MY_LIST_DATA) is
      -- Append arg to the end of self.
      new_elt::=#MY_LIST_ELEMENT(data:=arg);
      if head=void then head:=new_elt
      else tail.next:=new_elt end;
      tail:=new_elt end;

   print is
      -- Print the data held in the list on OUT.
      elt::=head;
      loop while!(elt/=void); elt.data.print;
     elt:=elt.next end end;
end;
-------------------------------------------------------------------
class MY_NUMBER is
   -- An integer value that can be held in a MY_LIST.
   inherit $MY_LIST_DATA;

   attr value:INT;

   print is
      -- Print the integer value on OUT.
      #OUT + "Number: " + value + "\n" end;
end;
-------------------------------------------------------------------
class MY_POINT is
   -- A two-dimensional point that can be held in a MY_LIST.
   inherit $MY_LIST_DATA;

   attr x,y:INT;

   print is
      -- Print the point coordinates on OUT.
      #OUT + "Point: " + x + "," + y + "\n" end;
end;
-------------------------------------------------------------------
class MY_LIST_TEST is
   -- Test of MY_LIST.

   main is
      -- Add some numbers and points to lists and print them out.
      list1::=#MY_LIST; list2::=#MY_LIST;
      n1::=#MY_NUMBER(10); n2::=#MY_NUMBER(20);
      p1::=#MY_POINT(2,3); p2::=#MY_POINT(4,5);
      -- Build the lists:
      list1.add_to_list(n1); list1.add_to_list(n2);
      list1.add_to_list(p1);
      list2.add_to_list(n2);    -- An object can be in more than one list.
      list2.add_to_list(p1); list2.add_to_list(p2);
      list2.add_to_list(list1); -- Put a list in another list.
       -- Print the lists
      #OUT + "list1:\n";
      list1.print;
      #OUT + "list2:\n";
      list2.print end;
end;


[LISTING ELEVEN]


Beta Implementation by Steve Mann (MADA)

--- program:descriptor---
(#
   MyListData: (#  Print:< (# do INNER #)  #);
   MyListElement: (#   data: yListData;  next: yListElement;   #);
   MyList: MyListData (#
       head, tail: yListElement;
   AddToList: (#
       newItem: yListData
       enter newItem[]
       do  (if head[] = NONE // TRUE then
           &MyListElement[] -> head[] -> tail[];
           newItem[] -> head.data[];
          else
           &MyListElement[]-> tail.next[] -> tail[];
           newItem[] -> tail.data[];
          if);
   #);
   Print::< (#
       element: yListElement
       do head[] -> element[];
           loop    (# while::< (# do element[] <> NONE -> value #);
       do  element.data.print; element.next[] -> element[];
   #); #); #);
   MyNumber: MyListData (#
   value: @integer;
        Print::< (# do 'Number: ' -> PutText; value -> PutInt; NewLine; #);
        enter value
    #);
   MyPoint: MyListData (#
   x, y: @integer;
        Print::< (# do  'Point: ' -> PutText;   x -> PutInt;
        ', ' -> PutText;    y -> PutInt;NewLine;
     #);
     enter (x, y)
     #);
(********************** MAIN **************************)
   list1, list2: @MyList;
   n1, n2: @MyNumber;  p1, p2: @MyPoint;
do
   10 -> n1;   20 -> n2;
   (2, 3) -> p1;   (4, 5) -> p2;

   n1[] -> list1.AddToList;n2[] -> list1.AddToList;
   p1[] -> list1.AddToList;

   n2[] -> list2.AddToList;p1[] -> list2.AddToList;
   p2[] -> list2.AddToList;list1[] -> list2.AddToList;

   'list1: ' -> PutLine;   list1.Print;
   'list2: ' -> PutLine;   list2.Print;
#)


[LISTING TWELVE]


Drool Implementation by David Betz (Dr. Dobb's Journal)


(defobject MyListElement ()
  (property next nil
            data nil))
(defobject MyList ()
  (property head nil
            tail nil))
(defmethod (MyList 'AddToList data)
  (let ((newElement (clone MyListElement 'data data))
        (tail (getp self 'tail)))
    (if tail
      (setp! tail 'next newElement)
      (setp! self 'head newElement))
    (setp! self 'tail newElement)))
(defmethod (MyList 'Print)
  (let ((element (getp self 'head)))
    (while element
      ((getp element 'data) 'Print)
      (set! element (getp element 'next)))))
(defobject MyNumber ()
  (property value 0))
(defmethod (MyNumber 'Print)
  (print "Number: " (getp self 'value) "\n"))
(defobject MyPoint ()
  (property x 0
            y 0))
(defmethod (MyPoint 'Print)
  (print "Point: " (getp self 'x) "," (getp self 'y) "\n"))
(define (main)
  (let ((list1 (clone MyList))
        (list2 (clone MyList))
        (n1 (clone MyNumber 'value 10))
        (n2 (clone MyNumber 'value 20))
        (p1 (clone MyPoint 'x 2 'y 3))
        (p2 (clone MyPoint 'x 4 'y 5)))
    (list1 'AddToList n1)
    (list1 'AddToList n2)
    (list1 'AddToList p1)
    (list2 'AddToList n2)
    (list2 'AddToList p1)
    (list2 'AddToList p2)
    (list2 'AddToList list1)
    (print "list1\n")
    (list1 'Print)
    (print "list2\n")
    (list2 'Print)))


[LISTING THIRTEEN]



Ada Implementation by Mike Ruf (Rational)

-- Ada83 does not support run-time polymorphism, so only supports homogeneous
-- lists. Ada9X will run-time polymorphism. There are ways to simulate run-time
-- polymorphism in Ada83, which is discussed in: Seidewitz, Ed,
-- "Object-Oriented Programming Through Type Extension in Ada 9X," Ada
-- Letters, Volume 11, Number 2, Mar/Apr 1991, pages 86-97. Hirasuna, Michael,
-- "Using Inheritance and Polymorphism with Ada in Government Sponsored
-- Contracts", Ada Letters, Volume 12, Number 2, Mar/Apr, 1992, pp. 43-56.
-- Mike Ruf, Rational Systems
-- This package implements a singly-linked list abstraction.
--
generic
    type Element is private;
    with procedure Print (This : Element);
package Printable_List_Generic is
    type List is private;
    function New_List return List;
    -- Returns an empty list.
    procedure Append (To_List : in out List;
                      This_Element : in Element);
    -- Propagates the Storage_Error exception if it occurs.
    procedure Print (This : in List);
    -- For each element in the list, calls the Print procedure,
    -- which is supplied as a generic formal parameter.
private
    type Node;
    type Pointer is access Node;
    type Node is
        record
            Contents : Element;
            Next : Pointer := null;
        end record;
    type List is
        record
            First : Pointer := null;
            Last : Pointer := null;
        end record;
end Printable_List_Generic;
package body Printable_List_Generic is
    function New_List return List is
    begin
        return (List'(First => null, Last => null));
    end New_List;
    procedure Append (To_List : in out List;
                      This_Element : in Element) is
       New_Node : Pointer := new Node'(Contents => This_Element, Next => null);
       -- The "next" component of the last node is always "null"
    begin
        if To_List.First = null then
            To_List.First := New_Node;
            To_List.Last := New_Node;
        else
            To_List.Last.Next := New_Node;
            To_List.Last := New_Node;
        end if;
    end Append;
    procedure Print (This : in List) is
        Current : Pointer := This.First;
    begin
        while (not (Current = null)) loop
            Print (Current.Contents);
            Current := Current.Next;
        end loop;
    end Print;
end Printable_List_Generic;

package Geometry is
    type Number is new Integer;
    procedure Print (This : Number);
    type Point is
        record
            X : Number := 0;
            Y : Number := 0;
        end record;
    procedure Print (This : Point);
end Geometry;

package body Geometry is
    procedure Print (This : Number) is
    begin
        Text_Io.Put_Line ("Number:" & Number'Image (This));
    end Print;
    procedure Print (This : Point) is
    begin
        Text_Io.Put_Line ("Point: " & Number'Image (This.X) &
                          "," & Number'Image (This.Y));
    end Print;
end Geometry;

procedure List_Example;

with Geometry;
with Printable_List_Generic;

procedure List_Example is
    package Numbers is new Printable_List_Generic (Element => Geometry.Number,
                                                   Print => Geometry.Print);
    package Points is new Printable_List_Generic (Element => Geometry.Point,
                                                  Print => Geometry.Print);
    Number_List : Numbers.List := Numbers.New_List;
    Point_List : Points.List := Points.New_List;
    N1 : Geometry.Number := 10;
    N2 : Geometry.Number := 20;
    P1 : Geometry.Point := (2, 3);
    P2 : Geometry.Point := (4, 5);
begin
    -- Build the lists
    Numbers.Append (To_List => Number_List, This_Element => N1);
    Numbers.Append (To_List => Number_List, This_Element => N2);
    --
    Points.Append (To_List => Point_List, This_Element => P1);
    Points.Append (To_List => Point_List, This_Element => P2);
    -- Print the lists
    Numbers.Print (Number_List);
    Points.Print (Point_List);
end List_Example;

------------------------------- OUTPUT  -------------------------------
Number: 10
Number: 20
Point:  2, 3
Point:  4, 5



Copyright © 1993, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.