flist.sa
Generated by gen_html_sa_files from ICSI. Contact gomes@icsi.berkeley.edu for details
-------------------------> GNU Sather - sourcefile <-------------------------
-- Copyright (C) 1994 by International Computer Science Institute --
-- This file is part of the GNU Sather library. It is free software; you may --
-- redistribute and/or modify it under the terms of the GNU Library General --
-- Public License (LGPL) as published by the Free Software Foundation; --
-- either version 2 of the license, or (at your option) any later version. --
-- This library is distributed in the hope that it will be useful, but --
-- WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See Doc/LGPL for more details. --
-- The license text is also available from: Free Software Foundation, Inc., --
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --
--------------> Please email comments to <bug-sather@gnu.org> <--------------
-- flist.sa: Array-based lists of elements of type T.
-- Jan4/96 - incorporated Erik Schnetter's changes.
class FLIST{T} < $ARR{T}
class FLIST{T} < $ARR{T} is
-- Array-based lists of elements of type T. These are extensible
-- stacks based on amortized doubling. They may often be used as
-- replacements for linked lists. Like linked lists (which are
-- widely used as containers in languages like Lisp), they serve as
-- general container objects for holding collections of other
-- objects. They are often a more efficient abstraction, however,
-- because less allocation and deallocation must occur, because
-- they keep successive elements in successive memory locations,
-- because they don't require storage for the links in a linked
-- list, and they support efficient access by array index. Linked
-- lists also support insertion and deletion into the middle of the
-- list. The set operations `union', `intersection', `difference',
-- and `sym_difference' and the searching operation `index_of' are
-- implemented by brute force search. If extensive use is made of
-- these operations, one should consider the use of other data
-- structures such as FSET{T}.
include COMPARE{T};
private include AREF{T} aget->private aref_aget,
aset->private aref_aset; -- Storage for the stack elements.
private attr loc:INT; -- The index to insert the next element.
-- It would be appropriate to verify the value of "loc" with an
-- invariant here, but for efficiency we want to be able to destroy
-- old objects after a size change occurs (requiring the
-- writeback). Such calls destroy self, and so the invariant can't
-- be called.
-- invariant:BOOL is
-- if void(self) then return true end;
-- else return loc.is_bet(0,asize) and asize>0 end;
create:SAME is return void; end;
create(n:INT):SAME
-- A new empty FLIST capable of storing `n' elements without extra
-- space allocation.
pre n>=0 is
if n=0 then return void
else return new(n)
end;
end;
create(a: ARRAY{T}): SAME is
-- Create a new FLIST from the elements in the array "a"
-- Useful for using the array shorthand for specifying the elements
sz ::= a.size;
res ::= new(sz);
res.loc := sz;
i ::= 0; loop until!(i = sz); res[i] := a[i]; i := i + 1; end;
return res;
end;
create_from(a: $ELT{T}): SAME is
-- Create from any container
res ::= #SAME;
loop res := res.push(a.elt!) end;
return res;
end;
create_empty_sized(n: INT): SAME
-- Create an flist with n elements that are set to elt_nil
pre n >= 0
is
res ::= create(n);
res.loc := n;
loop res.aset!(res.elt_nil) end;
return res;
end;
copy:SAME is
-- A copy of self.
if void(self) then return void end;
r::=new(asize);
i ::= 0; sz ::= loc;
r.loc := loc;
loop until!(i = sz); r[i] := [i]; i := i + 1; end;
return r;
-- loop r:=r.push(elt!) end; return r
end;
-- **** Insertion and deletion
push(e:T):SAME is
-- Add a new element to the end of the list and return the list.
-- If self is void, create a new list. Usage: `l:=l.push(e)'.
r:SAME;
if void(self) then r:=new(5)
elsif loc<asize then r:=self
else r:=new(2*asize); r.loc:=loc;
loop r.aset!(elt!) end;
-- clear
SYS::destroy(self); -- The old one should never be used.
end;
r.loc:=r.loc+1; r[r.loc-1]:=e;
return r
end;
pop:T is
-- Return the top element and shrink the list.
-- Void if the list is empty or void.
if size=0 then return void end;
r::=[loc-1]; [loc-1]:=void; loc:=loc-1; return r
end;
top:T is
-- The value of the top of the list.
-- Void if the list is empty or void.
if size=0 then return void end;
return [loc-1]
end;
fill(e: T) is
loop set!(e) end;
end;
clear is
-- Clear the list. Self may be void. Clear array elements
-- so they won't be referenced any more (and may become garbage).
if is_empty then return
else
nil: T;
loop [size.times!]:= nil end;
loc:=0;
end;
end;
reset is
-- Semantically identical to clear, but don't reset array
-- values (space may not be freed). Useful for quickly
-- emptying the list when you know it won't matter.
if ~void(self) then loc := 0 end;
end;
push_if_new(e:T):SAME is
-- Push `e' if it is not already present in the list.
-- Self may be void.
-- Usage is: `l:=l.push_if_new(e)'. Consider using FSET{T}.
if has(e) then return self else return push(e) end
end;
append(l:SAME):SAME
-- Append `l' to the end of self and return the result.
-- Self may be void. `l' mustn't equal self unless void.
-- Modified(ben) - hopefully much more efficient - no iters
pre ~SYS::ob_eq(l,self) or void(self) is
r::=copy;
old_size ::= size;
r := r.expand_to_size(size+l.size);
i ::= old_size; sz ::= old_size+l.size;
li ::= 0;
loop until!(i=sz);
r[i] := l[li];
li := li+1;
i := i + 1;
end;
return r
end;
concat(l:SAME):SAME
-- Append 'l' destructively. 'l' mustn't equal self
-- unless void.
-- Modified (ben) - hopefully more efficient - no iters, single alloc
pre ~SYS::ob_eq(l,self) or void(self) is
res::=self;
if ~void(l) then
oldsize ::= size;
res := res.expand_to_size(size+l.size);
i ::= 0; sz ::= l.size;
resi ::= oldsize;
loop until!(i = sz);
res[resi] := l[i];
i := i + 1;
resi := resi+1;
end;
-- Old version: res:=res.push(l.elt!) end
end;
return (res);
end;
-- Users are advised to use this first set of routines
-- since these may later be rewritten to allow the FLIST
-- to shrink, which the versions without return values cannot do.
delete(ind: INT): SAME is delete(ind); return self end;
delete_elt(e: T): SAME is delete_elt(e); return self end;
delete_ordered(ind: INT): SAME is delete_ordered(ind); return self end;
delete_elt_ordered(e: T): SAME is delete_elt_ordered(e); return self end;
delete(ind:INT)
-- Delete the element with index `ind' and move the last element in
-- its place. Self may not be void.
pre ~void(self) and ind.is_bet(0,loc-1)
is
[ind]:=[loc-1]; loc := loc - 1;
end;
delete_elt(e: T) is delete(index_of(e)) end;
-- Delete first occurance of element e from the list. Consider
-- using FSET.
delete_ordered(ind: INT)
-- Delete the element with index `ind' and move up all other
-- elements (thus preseving order). More expensive than
-- 'delete'. Self may not be void.
pre ~void(self) and ind.is_bet(0,loc-1) is
i ::= ind+1; loop until!(i>=size);
[i-1] := [i];
i := i+1;
end;
loc := loc -1;
end;
delete_elt_ordered(e: T) is delete_ordered(index_of(e)) end;
-- Similar to delete_ord, but for the element "e"
insert_after(ind:INT, val:T): SAME pre valid_after_ind(ind) is
-- Insert the value "val" after the index "ind".
-- push all later elements upwards.
r: SAME := expand_to_size(size+1);
-- Then move all elements downwards
r.push_downward(ind+1,1);
r[ind+1] := val;
return r;
end;
insert_before(ind:INT, val:T): SAME pre valid_before_ind(ind) is
-- Insert val just before index "ind"
r: SAME := expand_to_size(size+1);
-- Then move all elements downwards, including the elt at "ind"
r.push_downward(ind,1);
r[ind] := val;
return r;
end;
insert_all_after(ind:INT, val:$CONTAINER{T}):SAME
pre valid_after_ind(ind) is
-- Insert all the values in "val" after the element at index
-- "ind" in the order in which they are yielded by "val.elt!"
r: SAME := expand_to_size(size+val.size);
r.push_downward(ind+1,val.size);
i ::= ind+1; loop r[i] := val.elt!; i := i + 1; end;
return r
end;
insert_all_before(ind:INT, val:$CONTAINER{T}) :SAME
-- Insert all the values in "val" before the element in self at
-- index "ind" in the order in which they are yielded by "val.elt!"
pre valid_before_ind(ind)
is
r: SAME := expand_to_size(size+val.size);
r.push_downward(ind,val.size);
i ::= ind; loop r[i] := val.elt!; i := i + 1; end;
return r
end;
-- **** Predicates
contains(e: T): BOOL is return has(e) end;
has(e:T):BOOL is
-- True if `e' is contained in self.
loop if elt_eq(e,elt!) then return true end end;
return false
end;
is_empty:BOOL is
-- True if the list is empty or void.
return size=0
end;
equals(l: $CONTAINER{T}): BOOL is
-- Return true if the elemetns of "l" are the same as the elements
-- of self
typecase l
when SAME then
if size /= l.size then return false end;
loop
if ~elt_eq(elt!,l.elt!) then return false end;
end;
return true;
else return false; end;
end;
has_ind(i: INT): BOOL is
-- Return true if "i" is a valid index in this FLIST
return 0 <= i and i < size
end;
valid_after_ind(i: INT): BOOL is return -1 <= i and i < size end;
valid_before_ind(i:INT): BOOL is return 0 <=i and i <= size end;
-- **** Access functions
size:INT is
-- The current size. Self may be void.
if void(self) then return 0 else return loc end
end;
aget(ind:INT):T
-- The element of self with index `ind'. Self may not be void.
pre ~void(self) and ind.is_bet(0,loc-1)
is
return aref_aget(ind)
end;
aset(ind:INT,val:T)
-- Set the element of self with index `ind' to `val'. Self may
-- not be void.
pre ~void(self) and ind.is_bet(0,loc-1)
is
aref_aset(ind,val)
end;
index_of(e:T):INT is
-- The list index of `e'. -1 if the list is void or the
-- element is not present (not fast). Consider using FSET{T}.
if ~void(self) then
loop r::=ind!; if elt_eq(e,[r]) then return r end end
end;
return -1
end;
-- **** Iterators
elt!:T is
-- Yield the elements of self in order. Self may be void.
-- Don't insert elements while calling this.
-- Modified (ben) - must ask Claudio
if ~void(self) then
i ::= 0; sz ::= loc;
loop until!(i = sz); yield [i]; i := i + 1; end;
end;
end;
elt!(once beg:INT):T
-- Yield the elements of self starting at `beg'.
-- Don't insert elements while calling this.
-- Modified (ben) - Looked at fast version - does
-- not seem to be optimized out. Must ask Claudio about this
pre ~void(self) and beg.is_bet(0,loc-1)
is
i ::= beg; sz ::= loc;
loop until!(i = sz); yield [i]; i := i + 1; end;
end;
-- loop yield aelt!(beg,loc-beg) end end;
elt!(once beg,once num:INT):T
-- Yield `num' successive elements starting at index `beg'.
-- Don't insert elements while calling this.
pre ~void(self) and beg.is_bet(0,loc-1) and
num.is_bet(0,loc-beg) is
i ::= beg; sz ::= loc.min(beg+num);
loop until!(i = sz); yield [i]; i := i + 1; end;
end;
elt!(once beg,once num,once step:INT):T
-- Yield `num' elements starting at `beg' stepping by `step'.
pre ~void(self) and is_legal_iteration(beg,num,step,loc) is
loop yield aelt!(beg,num,step) end
end;
private is_legal_iteration(beg,num,step:INT,container_size:INT): BOOL is
-- True if the arguments are legal for an iteration from
-- index beg, with a step of "step" for "num" iterators
-- in a container of size "container_size"
if ~beg.is_bet(0,container_size-1) then return false end;
if step>0 then return num.is_bet(0,(container_size-1-beg+step)/step);
elsif step<0 then return num.is_bet(0,(beg-step)/-step);
else return false end
end;
set!(e: T) is
loop aset!(e); yield; end;
end;
ind!:INT is
if ~void(self) then
loop yield 0.upto!(loc-1) end
end
end;
-- **** Conversion
array:ARRAY{T} is
-- An array containing the elements of self. Void if self is void.
if void(self) then return void end;
r::=#ARRAY{T}(loc);
loop r.set!(elt!) end; return r
end;
inds: ARRAY{INT} is
-- Return an array consisting of the indices of the FLIST
res ::= #ARRAY{INT}(size);
loop res.set!(size.times!) end;
return res;
end;
str: STR is
-- Prints out a string version of the flist of the components
-- that are under $STR
if void(self) then return "{}" else return ELT_ALG{T}::str(self) end;
end;
as_array:ARRAY{T} is
res ::= #ARRAY{T}(size);
loop res.set!(elt!) end;
return res;
end;
-- **** Non modifying operators
union(l:SAME):SAME is
-- A new list containing the elements in self unioned with
-- those in `l'. Doesn't modify self or `l'. Self may be void.
-- Consider using FSET{T} for better performance.
r::=copy; loop r:=r.push_if_new(l.elt!) end; return r
end;
intersect(l:SAME):SAME is
-- A new list containing the elements in both self and `l'.
-- Doesn't modify self or `l'. Consider FSET{T} for better
-- performance. Self may be void.
r:SAME;
loop e::=elt!; if l.has(e) then r:=r.push(e) end end;
return r
end;
difference(l:SAME):SAME is
-- A new list containing the elements of self not in `l'.
-- Doesn't modify self or `l'. Consider FSET{T} for better
-- performance. Self may be void.
r:SAME;
loop e::=elt!; if ~l.has(e) then r:=r.push(e) end end;
return r
end;
sym_difference(l:SAME):SAME is
-- A new list containing the elements in self or `l' but
-- not both. Doesn't modify self or `l'. Consider FSET{T} for
-- better performance. Self may be void.
r:SAME;
loop e::=elt!; if ~l.has(e) then r:=r.push(e) end end;
loop e::=l.elt!; if ~has(e) then r:=r.push(e) end end;
return r
end;
sublist(beg,num:INT):SAME
-- A new list with `num' entries copied from self starting
-- at `beg'. Self may not be void.
pre ~void(self) and
beg.is_bet(0,loc-1) and num.is_bet(0,loc-beg) is
r::=new(num+5); r.loc:=num; r.acopy(0,num,beg,self); return r
end;
-- **** Modifying operators
to_reverse is
-- Reverse the order of the elements in self. Self may be void.
if void(self) then return end;
loop i::=(loc/2).times!;
u::=loc-i-1; t::=[i]; [i]:=[u]; [u]:=t
end
end;
-- ------------------- Implementation ------------------
private push_downward(from_ind: INT, by: INT) pre from_ind >= 0 is
-- Push all the elements from index "from_ind" downward by
-- "ind" spots. The last elements are pushed off the end
to ::= size-1;
from ::= size-by-1;
-- if size = 0 then return; end;
loop until!(from < from_ind);
[to] := [from];
from := from - 1; -- Increments should be faster than using
to := to - 1; -- just one index and offseting it.
end;
end;
private expand_to_size(new_size: INT): SAME is
-- Expand space so that the result has space for "new_size" elements.
-- Then set the location to new_size, indicating that it is filled
-- After this is done, the resulting array will be of size = new_size
-- and will have all the old elements of "self" copied over
-- and the remaining elements (if any) void
r: SAME;
if void(self) then r:=new(5.max(new_size))
elsif new_size<=asize then r:=self
else r:=new((2*asize).max(new_size));
r.loc:=loc; -- Necessary?
i ::= 0; sz ::= size; -- Copy over existing elements in self
loop until!(i = sz);
r[i] := [i] ;
i := i + 1;
end;
SYS::destroy(self); -- The old one should never be used.
end;
r.loc := new_size;
return r
end;
count(e:T):INT is
-- Return the number of occurences of 'e' in self
res:INT := 0;
loop i ::= elt!; if elt_eq(e,i) then res := res+1; end; end;
return res;
end;
end; -- class FLIST{T}