skiplist.tcl

Go to the documentation of this file.
00001 /*  skiplist.tcl --*/
00002 /* */
00003 /*  Implementation of a skiplist data structure for Tcl.*/
00004 /* */
00005 /*  To quote the inventor of skip lists, William Pugh:*/
00006 /*      Skip lists are a probabilistic data structure that seem likely*/
00007 /*      to supplant balanced trees as the implementation method of*/
00008 /*      choice for many applications. Skip list algorithms have the*/
00009 /*      same asymptotic expected time bounds as balanced trees and are*/
00010 /*      simpler, faster and use less space.*/
00011 /* */
00012 /*  For more details on how skip lists work, see Pugh, William. Skip*/
00013 /*  lists: a probabilistic alternative to balanced trees in*/
00014 /*  Communications of the ACM, June 1990, 33(6) 668-676. Also, see*/
00015 /*  ftp://ftp.cs.umd.edu/pub/skipLists/*/
00016 /*  */
00017 /*  Copyright (c) 2000 by Keith Vetter*/
00018 /*  This software is licensed under a BSD license as described in tcl/tk*/
00019 /*  license.txt file but with the copyright held by Keith Vetter.*/
00020 /* */
00021 /*  TODO:*/
00022 /*  customize key comparison to a user supplied routine*/
00023 
00024 namespace ::struct {}
00025 
00026 namespace ::struct::skiplist {
00027     /*  Data storage in the skiplist module*/
00028     /*  -------------------------------*/
00029     /* */
00030     /*  For each skiplist, we have the following arrays*/
00031     /*    state - holds the current level plus some magic constants*/
00032     /*  nodes - all the nodes in the skiplist, including a dummy header node*/
00033     
00034     /*  counter is used to give a unique name for unnamed skiplists*/
00035     variable counter 0
00036 
00037     /*  Internal constants*/
00038     variable MAXLEVEL 16
00039     variable PROB .5
00040     variable MAXINT [expr {0x7FFFFFFF}]
00041 
00042     /*  commands is the list of subcommands recognized by the skiplist*/
00043     variable commands [list \
00044         "destroy"   \
00045         "delete"    \
00046         "insert"    \
00047         "search"    \
00048         "size"  \
00049         "walk"  \
00050         ]
00051 
00052     /*  State variables that can be set in the instantiation*/
00053     variable vars [list maxlevel probability]
00054     
00055     /*  Only export one command, the one used to instantiate a new skiplist*/
00056     namespace export skiplist
00057 }
00058 
00059 /*  ::struct::skiplist::skiplist --*/
00060 /* */
00061 /*  Create a new skiplist with a given name; if no name is given, use*/
00062 /*  skiplistX, where X is a number.*/
00063 /* */
00064 /*  Arguments:*/
00065 /*  name    name of the skiplist; if null, generate one.*/
00066 /* */
00067 /*  Results:*/
00068 /*  name    name of the skiplist created*/
00069 
00070 ret  ::struct::skiplist::skiplist (optional name ="" , type args) {
00071     set usage "skiplist name ?-maxlevel ##? ?-probability ##?"
00072     variable counter
00073     
00074     if { [llength [info level 0]] == 1 } {
00075     incr counter
00076     set name "skiplist${counter}"
00077     }
00078 
00079     if { ![string equal [info commands ::$name] ""] } {
00080     error "command \"$name\" already exists, unable to create skiplist"
00081     }
00082 
00083     # Handle the optional arguments
00084     set more_eval ""
00085     for {set i 0} {$i < [llength $args]} {incr i} {
00086     set flag [lindex $args $i]
00087     incr i
00088     if { $i >= [llength $args] } {
00089         error "value for \"$flag\" missing: should be \"$usage\""
00090     }
00091     set value [lindex $args $i]
00092     switch -glob -- $flag {
00093         "-maxl*" {
00094         set n [catch {set value [expr $value]}]
00095         if {$n || $value <= 0} {
00096             error "value for the maxlevel option must be greater than 0"
00097         }
00098         append more_eval "; set state(maxlevel) $value"
00099         }
00100         "-prob*" {
00101         set n [catch {set value [expr $value]}]
00102         if {$n || $value <= 0 || $value >= 1} {
00103             error "probability must be between 0 and 1"
00104         }
00105         append more_eval "; set state(prob) $value"
00106         }
00107         default {
00108         error "unknown option \"$flag\": should be \"$usage\""
00109         }
00110     }
00111     }
00112     
00113     # Set up the namespace for this skiplist
00114     namespace eval ::struct::skiplist::skiplist$name {
00115     variable state
00116     variable nodes
00117 
00118     # NB. maxlevel and prob may be overridden by $more_eval at the end
00119     set state(maxlevel) $::struct::skiplist::MAXLEVEL
00120     set state(prob) $::struct::skiplist::PROB
00121     set state(level) 1
00122     set state(cnt) 0
00123     set state(size) 0
00124 
00125     set nodes(nil,key) $::struct::skiplist::MAXINT
00126     set nodes(header,key) "---"
00127     set nodes(header,value) "---"
00128 
00129     for {set i 1} {$i < $state(maxlevel)} {incr i} {
00130         set nodes(header,$i) nil
00131     }
00132     } $more_eval
00133 
00134     # Create the command to manipulate the skiplist
00135     interp alias {} ::$name {} ::struct::skiplist::SkiplistProc $name
00136 
00137     return $name
00138 }
00139 
00140 /* */
00141 /*  Private functions follow*/
00142 
00143 /*  ::struct::skiplist::SkiplistProc --*/
00144 /* */
00145 /*  Command that processes all skiplist object commands.*/
00146 /* */
00147 /*  Arguments:*/
00148 /*  name    name of the skiplist object to manipulate.*/
00149 /*  args    command name and args for the command*/
00150 /* */
00151 /*  Results:*/
00152 /*  Varies based on command to perform*/
00153 
00154 ret  ::struct::skiplist::SkiplistProc (type name , optional cmd ="" , type args) {
00155     # Do minimal args checks here
00156     if { [llength [info level 0]] == 2 } {
00157     error "wrong # args: should be \"$name option ?arg arg ...?\""
00158     }
00159     
00160     # Split the args into command and args components
00161     if { [llength [info commands ::struct::skiplist::_$cmd]] == 0 } {
00162     variable commands
00163     set optlist [join $commands ", "]
00164     set optlist [linsert $optlist "end-1" "or"]
00165     error "bad option \"$cmd\": must be $optlist"
00166     }
00167     eval [linsert $args 0 ::struct::skiplist::_$cmd $name]
00168 }
00169 
00170 /*  ::struct::skiplist::_destroy --*/
00171 /* */
00172 /*  Destroy a skiplist, including its associated command and data storage.*/
00173 /* */
00174 /*  Arguments:*/
00175 /*  name    name of the skiplist.*/
00176 /* */
00177 /*  Results:*/
00178 /*  None.*/
00179 
00180 ret  ::struct::skiplist::_destroy (type name) {
00181     namespace delete ::struct::skiplist::skiplist$name
00182     interp alias {} ::$name {}
00183 }
00184 
00185 /*  ::struct::skiplist::_search --*/
00186 /* */
00187 /*  Searches for a key in a skiplist*/
00188 /* */
00189 /*  Arguments:*/
00190 /*  name        name of the skiplist.*/
00191 /*  key     key for the node to search for*/
00192 /* */
00193 /*  Results:*/
00194 /*  0 if not found*/
00195 /*  [list 1 node_value] if found*/
00196 
00197 ret  ::struct::skiplist::_search (type name , type key) {
00198     upvar ::struct::skiplist::skiplist${name}::state state
00199     upvar ::struct::skiplist::skiplist${name}::nodes nodes
00200 
00201     set x header
00202     for {set i $state(level)} {$i >= 1} {incr i -1} {
00203     while {1} {
00204         set fwd $nodes($x,$i)
00205         if {$nodes($fwd,key) == $::struct::skiplist::MAXINT} break
00206         if {$nodes($fwd,key) >= $key} break
00207         set x $fwd
00208     }
00209     }
00210     set x $nodes($x,1)
00211     if {$nodes($x,key) == $key} {
00212     return [list 1 $nodes($x,value)]
00213     }
00214     return 0
00215 }
00216 
00217 /*  ::struct::skiplist::_insert --*/
00218 /* */
00219 /*  Add a node to a skiplist.*/
00220 /* */
00221 /*  Arguments:*/
00222 /*  name        name of the skiplist.*/
00223 /*  key     key for the node to insert*/
00224 /*  value       value of the node to insert*/
00225 /* */
00226 /*  Results:*/
00227 /*  0      if new node was created*/
00228 /*        level  if existing node was updated*/
00229 
00230 ret  ::struct::skiplist::_insert (type name , type key , type value) {
00231     upvar ::struct::skiplist::skiplist${name}::state state
00232     upvar ::struct::skiplist::skiplist${name}::nodes nodes
00233     
00234     set x header
00235     for {set i $state(level)} {$i >= 1} {incr i -1} {
00236     while {1} {
00237         set fwd $nodes($x,$i)
00238         if {$nodes($fwd,key) == $::struct::skiplist::MAXINT} break
00239         if {$nodes($fwd,key) >= $key} break
00240         set x $fwd
00241     }
00242     set update($i) $x
00243     }
00244     set x $nodes($x,1)
00245 
00246     # Does the node already exist?
00247     if {$nodes($x,key) == $key} {
00248     set nodes($x,value) $value
00249     return 0
00250     }
00251 
00252     # Here to insert item
00253     incr state(size)
00254     set lvl [randomLevel $state(prob) $state(level) $state(maxlevel)]
00255 
00256     # Did the skip list level increase???
00257     if {$lvl > $state(level)} {
00258     for {set i [expr {$state(level) + 1}]} {$i <= $lvl} {incr i} {
00259         set update($i) header
00260     }
00261     set state(level) $lvl
00262     }
00263 
00264     # Create a unique new node name and fill in the key, value parts
00265     set x [incr state(cnt)] 
00266     set nodes($x,key) $key
00267     set nodes($x,value) $value
00268 
00269     for {set i 1} {$i <= $lvl} {incr i} {
00270     set nodes($x,$i) $nodes($update($i),$i)
00271     set nodes($update($i),$i) $x
00272     }
00273 
00274     return $lvl
00275 }
00276 
00277 /*  ::struct::skiplist::_delete --*/
00278 /* */
00279 /*  Deletes a node from a skiplist*/
00280 /* */
00281 /*  Arguments:*/
00282 /*  name        name of the skiplist.*/
00283 /*  key     key for the node to delete*/
00284 /* */
00285 /*  Results:*/
00286 /*  1 if we deleted a node*/
00287 /*        0 otherwise*/
00288 
00289 ret  ::struct::skiplist::_delete (type name , type key) {
00290     upvar ::struct::skiplist::skiplist${name}::state state
00291     upvar ::struct::skiplist::skiplist${name}::nodes nodes
00292     
00293     set x header
00294     for {set i $state(level)} {$i >= 1} {incr i -1} {
00295     while {1} {
00296         set fwd $nodes($x,$i)
00297         if {$nodes($fwd,key) >= $key} break
00298         set x $fwd
00299     }
00300     set update($i) $x
00301     }
00302     set x $nodes($x,1)
00303 
00304     # Did we find a node to delete?
00305     if {$nodes($x,key) != $key} {
00306     return 0
00307     }
00308     
00309     # Here when we found a node to delete
00310     incr state(size) -1
00311     
00312     # Unlink this node from all the linked lists that include to it
00313     for {set i 1} {$i <= $state(level)} {incr i} {
00314     set fwd $nodes($update($i),$i)
00315     if {$nodes($fwd,key) != $key} break
00316     set nodes($update($i),$i) $nodes($x,$i)
00317     }
00318     
00319     # Delete all traces of this node
00320     foreach v [array names nodes($x,*)] {
00321     unset nodes($v)
00322     }
00323 
00324     # Fix up the level in case it went down
00325     while {$state(level) > 1} {
00326     if {! [string equal "nil" $nodes(header,$state(level))]} break
00327     incr state(level) -1
00328     }
00329 
00330     return 1
00331 }
00332 
00333 /*  ::struct::skiplist::_size --*/
00334 /* */
00335 /*  Returns how many nodes are in the skiplist*/
00336 /* */
00337 /*  Arguments:*/
00338 /*  name        name of the skiplist.*/
00339 /* */
00340 /*  Results:*/
00341 /*  number of nodes in the skiplist*/
00342 
00343 ret  ::struct::skiplist::_size (type name) {
00344     upvar ::struct::skiplist::skiplist${name}::state state
00345 
00346     return $state(size)
00347 }
00348 
00349 /*  ::struct::skiplist::_walk --*/
00350 /* */
00351 /*  Walks a skiplist performing a specified command on each node.*/
00352 /*  Command is executed at the global level with the actual command*/
00353 /*  executed is:  command key value*/
00354 /* */
00355 /*  Arguments:*/
00356 /*  name    name of the skiplist.*/
00357 /*  cmd     command to run on each node*/
00358 /* */
00359 /*  Results:*/
00360 /*  none.*/
00361 
00362 ret  ::struct::skiplist::_walk (type name , type cmd) {
00363     upvar ::struct::skiplist::skiplist${name}::nodes nodes
00364 
00365     for {set x $nodes(header,1)} {$x != "nil"} {set x $nodes($x,1)} {
00366     # Evaluate the command at this node
00367     set cmdcpy $cmd
00368     lappend cmdcpy $nodes($x,key) $nodes($x,value)
00369     uplevel 2 $cmdcpy
00370     }
00371 }
00372 
00373 /*  ::struct::skiplist::randomLevel --*/
00374 /* */
00375 /*  Generates a random level for a new node. We limit it to 1 greater*/
00376 /*  than the current level. */
00377 /* */
00378 /*  Arguments:*/
00379 /*  prob        probability to use in generating level*/
00380 /*  level       current biggest level*/
00381 /*  maxlevel    biggest possible level*/
00382 /* */
00383 /*  Results:*/
00384 /*  an integer between 1 and $maxlevel*/
00385 
00386 ret  ::struct::skiplist::randomLevel (type prob , type level , type maxlevel) {
00387 
00388     set lvl 1
00389     while {(rand() < $prob) && ($lvl < $maxlevel)} {
00390     incr lvl
00391     }
00392 
00393     if {$lvl > $level} {
00394     set lvl [expr {$level + 1}]
00395     }
00396     
00397     return $lvl
00398 }
00399 
00400 /*  ::struct::skiplist::_dump --*/
00401 /* */
00402 /*  Dumps out a skip list. Useful for debugging.*/
00403 /* */
00404 /*  Arguments:*/
00405 /*  name    name of the skiplist.*/
00406 /* */
00407 /*  Results:*/
00408 /*  none.*/
00409 
00410 ret  ::struct::skiplist::_dump (type name) {
00411     upvar ::struct::skiplist::skiplist${name}::state state
00412     upvar ::struct::skiplist::skiplist${name}::nodes nodes
00413 
00414 
00415     puts "Current level $state(level)"
00416     puts "Maxlevel:     $state(maxlevel)"
00417     puts "Probability:  $state(prob)"
00418     puts ""
00419     puts "NODE    KEY  FORWARD"
00420     for {set x header} {$x != "nil"} {set x $nodes($x,1)} {
00421     puts -nonewline [format "%-6s  %3s %4s" $x $nodes($x,key) $nodes($x,1)]
00422     for {set i 2} {[info exists nodes($x,$i)]} {incr i} {
00423         puts -nonewline [format %4s $nodes($x,$i)]
00424     }
00425     puts ""
00426     }
00427 }
00428 
00429 /*  ### ### ### ######### ######### #########*/
00430 /*  Ready*/
00431 
00432 namespace ::struct {
00433     /*  Get 'skiplist::skiplist' into the general structure namespace.*/
00434     namespace import -force skiplist::skiplist
00435     namespace export skiplist
00436 }
00437 package provide struct::skiplist 1.3
00438 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1