Example: The LinkedList Package - Maple Programming Help

Online Help

All Products    Maple    MapleSim


Home : Support : Online Help : Applications and Example Worksheets : Language and System : examples/LinkedListPackage

Example: The LinkedList Package

Introduction

  

This example worksheet contains a small package called LinkedList. It illustrates the basic structure of a package implemented by using modules.

Background

  

Linked lists are a basic data structure used in programs for many different purposes. There are many kinds of linked lists, with variations on the basic idea intended to address performance and functionality issues. The example package shown in this subsection provides a few operations on the simplest possible form of linked lists.

  

The links in a linked list are formed from a very simple data structured called a pair. A pair is essentially a container with space for exactly two elements. Pairs can be modeled by fixed length records with two slots. When used to implement linked lists, the first slot holds the data for the list entry, and the second slot stores a pointer to the next pair in the list.

  

The LinkedList package implements an abstract data definition for the pair data structure, and adds some higher level operations on pairs to effect the list abstraction. A linked list is effectively represented by its first pair.

  

The pair abstract data structure is very simple. It consists of a constructor, pair, and two accessors called head and tail that satisfy the algebraic specification

p=pairheadp,tailp

  

for each pair p. In addition, there is a distinguished pair nil, satisfying this algebraic relation, that is unequal to any other pair, and satisfies headnil=nil, tailnil=nil.

  

Note that linked lists are quite different from the Maple built-in list structures, which are really immutable arrays. Linked lists are best suited for applications in which you want to incrementally build up the list from its members.

  

Note: Lisp programmers will recognize the pair, head, and tail operations as the more traditional operations known as "cons", "car" and "cdr".

Package Implementation

  

The LinkedList package is implemented as a module containing the primitive operations on pairs, and higher level operations that implement the list abstraction.

macro( _PAIR = `` ): # for nice printing

LinkedList := module()
    description "routines for simple linked lists";
    export
        nil,
        nullp,
        pair,
        head,
        tail,
        list,
        length,
        member,
        reverse,
        append,
        map;
    local
        setup,
        cleanup,
        map1,
        reverse1,
        _PAIR;
    option
        package,
        load = setup,
    unload = cleanup;

    setup := proc()
        global  `type/Pair`, `type/LinkedList`;
        `type/Pair` := '{ _PAIR( anything, anything ),
                          identical( nil ) }';
        `type/LinkedList` := proc( expr )
            if expr = nil then
                true;
            elif type( expr, Pair ) then
                type( tail( expr ), 'LinkedList' );
            else
                false;
            end if;
        end proc;
        userinfo( 1, 'LinkedList',
           "new types `Pair' and `LinkedList' defined" );
        NULL;
    end proc;

    cleanup := proc()
        global  `type/Pair`, `type/LinkedList`;
        userinfo( 1, 'LinkedList',
            "cleaning up global types" );
        `type/Pair` := evaln( `type/Pair` );
        `type/LinkedList` := evaln( `type/LinkedList` );
        NULL;
    end proc;

    pair := ( a, b )
         -> setattribute( '_PAIR'( a, b ), 'inert' );
    head := ( c::Pair )
         -> `if`( c = nil, nil, op( 1, c ) );
    tail := ( c::Pair )
         -> `if`( c = nil, nil, op( 2, c ) );
    nullp := ( pair )
          -> evalb( pair = nil );

    list := proc()
        local   a, L;
        L := nil;
        for a in args do
            L := pair( a, L );
        end do;
    end proc;

    length := proc( lst )
        if nullp( lst ) then
            0;
        else
            1 + length( tail( lst ) );
        end if;
    end proc;

    member := proc( item, lst )
        if nullp( lst ) then
            false;
        elif item = head( lst ) then
            true;
        else
            thisproc( item, tail( lst ) );
        end if;
    end proc;

    map := proc( p, lst )
        if nullp( lst ) then
            nil;
        else
            pair( p( head( lst ) ),
                thisproc( p, tail( lst ) ) );
        end if;
    end proc;

    append := proc( lst1, lst2 )
        if nullp( lst1 ) then
            lst2;
        else
            pair( head( lst1 ),
                thisproc( tail( lst1 ), lst2 ) );
        end if;
    end proc;

    reverse1 := proc( sofar, todo )
        if nullp( todo ) then
            sofar;
        else
            thisproc( pair( head( todo ), sofar ),
                tail( todo ) );
        end if;
    end proc;

    reverse := lst -> reverse1( nil, lst );

    setup();

end module:

  

Normally, a package definition like this would be entered into a Maple source file using a text editor, or in a worksheet using the Maple graphical user interface. In either case, the definition would then be followed by a call to the savelib procedure using the name of the module as its sole argument:  savelib( 'LinkedList' ).

  

Evaluating the savelib call saves the module to the first repository found in the global variable libname, or the repository named with the global variable savelibname, if it is defined. (At least one of these must be defined.)

  

Important: Always ensure that the standard Maple library is write-protected to avoid saving expressions in it. If you accidentally save something to the standard Maple library, you may need to restore the original from the media on which you obtained the Maple software.

  

The package exports are listed as the exports of the module. A few local variables are used to implement the package. The local procedures map1 and reverse1 are part of the package implementation that is not available to users of the package. They are visible only within the module definition. This allows the package author to make improvements to the package without disturbing any code that uses it. If the local procedures reverse1 and map1 were exported (thus, available to users), it would be difficult for the author to replace these routines without breaking existing code that relies upon them.

  

The package includes two special (local) procedures, setup and cleanup. These are executed, respectively, when the module is first read from a repository, and when the package is either garbage collected or when Maple is about to exit.

Using the Package

  

The package exports can always be accessed by using the long form of their names.

p := LinkedList:-pair( a, b );

pa,b

(1)
  

For consistency with the older table-based package implementations, an indexed notation can also be used.

p := LinkedList[ 'pair' ]( a, b );

pa,b

(2)
  

This form requires that the index (in this case, the symbol pair) be protected from evaluation. Indexed notation does not extend to packages with nested subpackages.

  

To access the package exports interactively, use the with command.

with( LinkedList );

append,head,length,list,map,member,nil,nullp,pair,reverse,tail

(3)
  

Note that, since some of the package exports shadow global procedures with the same name, the with command issues warnings. These warnings are normal. They remind you that these names now refer to expressions different from the expressions to which they referred previously. Once the exports of the package LinkedList have been bound, you can call them as you would global Maple routines with those names. Note that you can still access the global version of member, for example, by using the syntax :-member.

use LinkedList in
   member( a, p );
   :-member( a, [ a, b, c, d ] );
end use;

true

true

(4)
  

This is one of the principal advantages of using modules and binding, rather than assignment, to implement packages.

  

Lists are either built incrementally using the pair export of the package, or by calling the list export.

L := nil:

for i from 1 to 10 do
    L := pair( i, L );
end do;

L1,nil

L2,1,nil

L3,2,1,nil

L4,3,2,1,nil

L5,4,3,2,1,nil

L6,5,4,3,2,1,nil

L7,6,5,4,3,2,1,nil

L8,7,6,5,4,3,2,1,nil

L9,8,7,6,5,4,3,2,1,nil

L10,9,8,7,6,5,4,3,2,1,nil

(5)

length( L );

10

(6)

member( 3, L );

true

(7)

member( 100, L );

false

(8)

reverse( L );

1,2,3,4,5,6,7,8,9,10,nil

(9)

sqL := map( x -> x^2, L );

sqL100,81,64,49,36,25,16,9,4,1,nil

(10)

member( 100, sqL );

true

(11)

L2 := list( a, b, c, d );

L2d,c,b,a,nil

(12)

map( sin, L2 );

sind,sinc,sinb,sina,nil

(13)

L3 := eval( L2, { a = 1, b = 2, c = 3, d = 4 } );

L34,3,2,1,nil

(14)

map( evalf[ 10 ], L3 );

4.,3.,2.,1.,nil

(15)

 

Return to the Index of Example Worksheets.

See Also

examples/GenericGraphAlgorithms