# # Skip Lists as an ordered set implementation (this version is sets) #
# For documentation of the data structure, see Wikipedia. A skip list is # essentially set of linked lists called levels -- in this implementation levels # currently start at 2. The list at level 2 contains all the objects in the set # in order, the lists at higher levels contain progressively fewer of them # (roughly 1/3 as many as at the level below). each level is a subset of the one # below. The nodes of the lists are the same objects, so that you can drop down # from one list to a lower level as you approach the object you want. # # # Each node is represented by a plain list whose 1 entry is the value at the # node and whose other entries are next pointers for all of the linked lists # that that entry is in. The end of the lists is represented by unbound. The # root object contains the head pointers of all the lists (and fail in position # 1) This object (along with the less than function and other attributes are # stored in an outer component object
# Record for local functions and constants to avoid cluttering the global # namespace
SKIPLISTS := rec();
SKIPLISTS.SkipListDefaultType := NewType(OrderedSetDSFamily, IsSkipListRep and IsOrderedSetDS and IsMutable);
SKIPLISTS.SkipListStandardType := NewType(OrderedSetDSFamily, IsSkipListRep and IsStandardOrderedSetDS and IsMutable);
SKIPLISTS.nullIterator := Iterator([]);
# This controls the relative sizes of the lists (i.e. how likely each entry is # to be promoted to the next list above the default value is 1/3 represented by # its inverse here. It is possible that 1/2 might be better choice when # comparison is very expensive.
SKIPLISTS.defaultInvProb := 3;
# # Worker function for all the constructors #
SKIPLISTS.NewSkipList := function(isLess, iter, rs, invprob)
local s, x, type; if isLess = \< then
type := SKIPLISTS.SkipListStandardType; else
type := SKIPLISTS.SkipListDefaultType; fi;
s := Objectify( type, rec(
lists := [fail],
isLess := isLess,
invprob := invprob,
size := 0,
randomSource := rs) ); for x in iter do
AddSet(s,x);
od;
return s; end;
# # Constructors #
InstallMethod(OrderedSetDS, [IsSkipListRep and IsOrderedSetDS and IsMutable, IsFunction], function(type, isLess)
return SKIPLISTS.NewSkipList(isLess, SKIPLISTS.nullIterator, GlobalMersenneTwister, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsOrderedSetDS and IsMutable], function(type)
return SKIPLISTS.NewSkipList(\<, SKIPLISTS.nullIterator, GlobalMersenneTwister, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsRandomSource], function(type, isLess, rs)
return SKIPLISTS.NewSkipList(isLess, SKIPLISTS.nullIterator, rs, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsListOrCollection], function(type, data)
return SKIPLISTS.NewSkipList(\<, Iterator(data), GlobalMersenneTwister, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsListOrCollection, IsRandomSource], function(type, data, rs)
return SKIPLISTS.NewSkipList(\<, Iterator(data), rs, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsOrderedSetDS], function(type, os)
return SKIPLISTS.NewSkipList(\<, Iterator(os), GlobalMersenneTwister, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsListOrCollection], function(type, isLess, data)
return SKIPLISTS.NewSkipList(isLess, Iterator(data), GlobalMersenneTwister, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsIterator], function(type, iter)
return SKIPLISTS.NewSkipList(\<, iter, GlobalMersenneTwister, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsIterator], function(type, isLess, iter)
return SKIPLISTS.NewSkipList(isLess, iter, GlobalMersenneTwister, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsListOrCollection, IsRandomSource], function(type, isLess, data, rs)
return SKIPLISTS.NewSkipList(isLess, Iterator(data), rs, SKIPLISTS.defaultInvProb); end);
InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsIterator, IsRandomSource], function(type, isLess, iter, rs)
return SKIPLISTS.NewSkipList(isLess, iter, rs, SKIPLISTS.defaultInvProb); end);
# # The general API doesn't provide a way to set this tuning parameter. #
BindGlobal("SetSkipListParameter", function(sl, p)
sl!.invprob := p; end);
# # This is the worker function which finds a value if it is present, and if not # finds where it would go. # # Specifically it returns a vector whose l entry is the skiplist entry at level # l which immediately precedes the value we are looking for (or would precede it # if it were present). So if it returns x with an entry in position l then # x[l][1] < val (or x[1] is the head node) and x[l][l] is either unbound or has # x[l][l][1] >= val
# # There is a C version of this function in skiplist.c. This version is retained # (and tested) as a reference implementation and/or if the kernel code is not # compiled. # # The first argyment is the head node, not the component object #
SKIPLISTS.ScanSkipListGAP := function(sl, val, less)
local level, ptr, lst, nx; # # We start at the head node and at the top level #
ptr := sl;
level := Length(ptr); # # the result will accumulate in lst #
lst := []; while level > 1do if not IsBound(ptr[level]) then # # end of the list at the current level, # remember this location and drop down #
lst[level] := ptr;
level := level -1; else # # Look ahead on current list #
nx := ptr[level]; if not less(nx[1],val) then # # the next node at the current level is AFTER or equal to the value we want # remember this location and drop down #
lst[level] := ptr;
level := level -1; else # # Move along at current level #
ptr := nx; fi; fi;
od;
return lst; end;
# # Use the C version if available # if IsBound(DS_Skiplist_Scan) then
SKIPLISTS.ScanSkipList := DS_Skiplist_Scan; else
SKIPLISTS.ScanSkipList := SKIPLISTS.ScanSkipListGAP; fi;
InstallMethod(AddSet, [IsSkipListRep and IsOrderedSetDS and IsMutable, IsObject], function(sl, val)
local lst, new, level, rs, ip, node; # # Scan and check #
lst := SKIPLISTS.ScanSkipList(sl!.lists,val, sl!.isLess); # # If the object is present, it will be the next item after the node returned by # the scan, at the lowest level (level 2) # # lst[2] unbound implies that the whole skiplist is actually empty # lst[2][2] unbound implies that we are about to add at the end of the skiplist # if IsBound(lst[2]) and IsBound(lst[2][2]) and lst[2][2][1] = val then # # It's there already #
return; fi;
# # Add the new value to as many linked lists as our random numbers dictate # # We now we'll need 2 entries in the list and 2/3 of the time that will be all, so # make a list with room for two entries #
new := EmptyPlist(2);
new[1] := val;
level := 2;
rs := sl!.randomSource;
ip := sl!.invprob; # # In this loop we add the new node to the linked list at level level # and decide whether to also add it at the next level up. # this may involve opening up a whole new level #
repeat if not IsBound(lst[level]) then # # New level #
Add(sl!.lists, new); else # # We're adding after node #
node := lst[level]; if IsBound(node[level]) then
new[level] := node[level]; fi;
node[level] := new; fi;
level := level+1;
until Random(rs, 1, ip) <> 1; # # Keep track of the size #
sl!.size := sl!.size+1; end);
InstallMethod(\in, [IsObject, IsSkipListRep and IsOrderedSetDS], function(val,sl)
local lst;
lst := SKIPLISTS.ScanSkipList(sl!.lists, val, sl!.isLess);
return IsBound(lst[2]) and IsBound(lst[2][2]) and lst[2][2][1] = val; end);
# # This is (profiling showed) the other loop worth moving into C brought # out a function. It deals with the mechanics of removing a node from all the # lists it's in # # lst is what is returned by the scan function, nx is the node to remove #
SKIPLISTS.RemoveNodeGAP := function(lst, nx)
local level, node; for level in [2..Length(lst)] do
node := lst[level]; if IsBound(node[level]) and IsIdenticalObj(node[level],nx) then if IsBound(nx[level]) then
node[level] := nx[level]; else # # In this case we are removing the last node # at this level #
Unbind(node[level]); fi; fi;
od; end;
# # Prefer the C implemnentation # if IsBound(DS_Skiplist_RemoveNode) then
SKIPLISTS.RemoveNode := DS_Skiplist_RemoveNode; else
SKIPLISTS.RemoveNode := SKIPLISTS.RemoveNodeGAP; fi;
# # And now what remains of the remove after the worker functions have been split out #
InstallMethod(RemoveSet, [IsSkipListRep and IsOrderedSetDS and IsMutable, IsObject], function(sl, val)
local lst, nx;
lst := SKIPLISTS.ScanSkipList(sl!.lists, val, sl!.isLess); if not IsBound(lst[2]) or not IsBound(lst[2][2]) then # # Wasn't there in the first place #
return 0; fi;
nx := lst[2][2]; if nx[1] <> val then # # Wasn't there in the first place #
return 0; fi;
SKIPLISTS.RemoveNode(lst, nx); # # Book-keeping #
sl!.size := sl!.size -1;
return 1; end);
# # Show the full structure of the skip list in a display # displays each linked list on a separate line. #
InstallMethod(DisplayString, [IsSkipListRep], function(sl)
local l, ptr, s;
s := [];
sl := sl!.lists; for l in [Length(sl),Length(sl)-1..2] do
Add(s,"->");
ptr := sl[l]; while true do
Add(s,String(ptr[1]));
Add(s,"->"); if not IsBound(ptr[l]) then
Add(s,"X\n");
break; else
ptr := ptr[l]; fi;
od;
od; if Length(s) = 0then
return "<empty skiplist>"; else
return Concatenation(s); fi;
end);
# # For inorder access to the skip list we can just ignore all the # lists except the one at level 2 which is an in-order SLL containing # all the elements. Hence the next couple of functions are pretty simple #
InstallMethod(Iterator, [IsSkipListRep and IsOrderedSetDS], function(sl)
return IteratorByFunctions(rec(
ptr := sl!.lists,
IsDoneIterator := function(iter)
return not IsBound(iter!.ptr[2]); end,
PrintObj := function(iter)
Print("Iterator of Skiplist"); end )); end);
# # A debugging function to actually count the nodes and make # sure our bookkepping is correct #
SKIPLISTS.CheckSize := function(sl)
local count, ptr;
count := 0;
ptr := sl!.lists; while IsBound(ptr[2]) do
count := count+1;
ptr := ptr[2];
od;
return count = Size(sl); end;
# # Since we keep track of it #
InstallMethod(Size, [IsSkipListRep and IsOrderedSetDS],
sl -> sl!.size);
# # Typical short view #
InstallMethod(ViewString, [IsSkipListRep and IsOrderedSetDS], function(sl)
return Concatenation(["<skiplist ",String(Size(sl))," entries>"]); end);
# # Try and make a string that will return the same object #
InstallMethod(String, [IsSkipListRep and IsOrderedSetDS], function(sl)
local s, isLess;
s := [];
Add(s,"OrderedSetDS(IsSkipListRep");
isLess := sl!.isLess; if isLess <> \< then
Add(s,", ");
Add(s,String(isLess)); fi; if not IsEmpty(sl) then
Add(s, ", ");
Add(s, String(AsList(sl))); fi;
Add(s,")");
return Concatenation(s); end);
# # Could do this faster, but copying the lists preserving all the structure # but not copying the entries would be fiddly #
InstallMethod(ShallowCopy, [IsSkipListRep and IsOrderedSetDS],
sl -> OrderedSetDS(IsSkipListRep, sl)
);
InstallMethod(LessFunction, [IsSkipListRep and IsOrderedSetDS],
sl -> sl!.isLess);
Messung V0.5 in Prozent
[Dauer der Verarbeitung: 0.28 Sekunden, vorverarbeitet 2026-10-11]