6 use vars qw($VERSION $docheck);
8 $VERSION = do { my @r = (q$Revision$ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r};
10 use constant NEXT => 0;
11 use constant PREV => 1;
12 use constant OBJ => 2;
18 confess("chain broken $_[1]") unless ref $_[0] && $_[0]->isa('Chain') &&
19 $_[0]->[PREV]->[NEXT] == $_[0] &&
20 $_[0]->[NEXT]->[PREV] == $_[0];
24 # set internal checking
36 push @$self, $self, $self, $ref;
37 return bless $self, $name;
40 # Insert before this point of the chain
45 $docheck && _check($p);
47 my $q = ref $ref && $ref->isa('Chain') ? $ref : new Chain $ref;
48 $q->[PREV] = $p->[PREV];
50 $p->[PREV]->[NEXT] = $q;
54 # Insert after this point of the chain
59 $docheck && _check($p);
61 $p->[NEXT]->ins($ref);
64 # Delete this item from the chain, returns the NEXT item in the chain
69 $docheck && _check($p);
71 $p->[PREV]->[NEXT] = $p->[NEXT];
72 $p->[NEXT]->[PREV] = $p->[PREV];
76 # Is this chain empty?
81 $docheck && _check($p);
83 return $p->[NEXT] == $p;
86 # return next item or undef if end of chain
91 $docheck && _check($base);
93 return $base->[NEXT] == $base ? undef : $base->[NEXT] unless $p;
95 $docheck && _check($p);
97 return $p->[NEXT] != $base ? $p->[NEXT] : undef;
100 # return previous item or undef if end of chain
105 $docheck && _check($base);
107 return $base->[NEXT] == $base ? undef : $base->[PREV] unless $p;
109 $docheck && _check($p);
111 return $p->[PREV] != $base ? $p->[PREV] : undef;
114 # return (and optionally replace) the object in this chain item
118 $p->[OBJ] = $ref if $ref;
122 # clear out the chain
126 while (!$base->isempty) {
131 # move this item after the 'base' item
136 $docheck && _check($base, "base") && _check($p, "rechained ref");
142 # count the no of items in a chain
149 ++$count while ($p = $base->next($p));
155 # Below is the stub of documentation for your module. You better edit it!
159 Chain - Double linked circular chain handler
167 $ref = $p->obj or $p->obj($ref);
168 $q = $base->next($p);
169 $q = $base->prev($p);
180 A module to handle those nasty jobs where a perl list simply will
181 not do what is required.
183 This module is a transliteration from a C routine I wrote in 1987, which
184 in turn was taken directly from the doubly linked list handling in ICL
185 George 3 originally written in GIN5 circa 1970.
187 The type of list this module manipulates is circularly doubly linked
188 with a base. This means that you can traverse the list backwards or
189 forwards from any point.
191 The particular quality that makes this sort of list useful is that you
192 can insert and delete items anywhere in the list without having to
193 worry about end effects.
195 The list has a I<base> but it doesn't have any real end! The I<base> is
196 really just another (invisible) list member that you choose to
197 remember the position of and is the reference point that determines
200 There is nothing special about a I<base>. You can choose another member
201 of the list to be a I<base> whenever you like.
203 The difference between this module and a normal list is that it allows
204 one to create persistant arbitrary directed graphs reasonably
205 efficiently that are easy to traverse, insert and delete objects. You
206 will never need to use I<splice>, I<grep> or I<map> again (for this
209 A particular use of B<Chain> is for connection maps that come and go
210 during the course of execution of your program.
212 An artificial example of this is:-
216 my $base = new Chain;
217 $base->ins({call=>'GB7BAA', users => new Chain});
218 $base->ins({call=>'GB7DJK', users => new Chain});
219 $base->ins({call=>'GB7MRS', users => new Chain});
221 # order is now GB7BAA, GB7DJK, GB7MRS
224 while ($p = $base->next($p)) {
226 if ($obj->{call} eq 'GB7DJK') {
227 my $ubase = $obj->{users};
228 $ubase->ins( {call => 'G1TLH'} );
229 $ubase->ins( {call => 'G7BRN'} );
230 } elsif ($obj->{call} eq 'GB7MRS') {
231 my $ubase = $obj->{users};
232 $ubase->ins( {call => 'G4BAH'} );
233 $ubase->ins( {call => 'G4PIQ'} );
234 } elsif ($obj->{call} eq 'GB7BAA') {
235 my $ubase = $obj->{users};
236 $ubase->ins( {call => 'G8TIC'} );
237 $ubase->ins( {call => 'M0VHF'} );
241 # move the one on the end to the beginning (LRU on a stick :-).
242 $base->rechain($base->prev);
244 # order is now GB7MRS, GB7BAA, GB7DJK
246 # this is exactly equivalent to :
251 # order is now GB7DJK, GB7MRS, GB7BAA
253 # disconnect (ie remove) GB7MRS
254 for ($p = 0; $p = $base->next($p); ) {
255 if ($p->obj->{call} eq 'GB7MRS') {
256 $p->del; # remove this 'branch' from the tree
257 $p->obj->{users}->flush; # get rid of all its users
266 Dirk Koopman <djk@tobit.co.uk>
270 ICL George 3 internals reference manual (a.k.a the source)