prioqueue.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 package require Tcl 8.2
00014
00015 namespace ::struct {}
00016
00017 namespace ::struct::prioqueue {
00018
00019 variable queues
00020
00021
00022 variable counter 0
00023
00024
00025 variable commands [list \
00026 "clear" \
00027 "destroy" \
00028 "get" \
00029 "peek" \
00030 "put" \
00031 "size" \
00032 "peekpriority" \
00033 ]
00034
00035 variable sortopt [list \
00036 "-integer" \
00037 "-real" \
00038 "-ascii" \
00039 "-dictionary" \
00040 ]
00041
00042
00043
00044
00045
00046 variable sortdir [list \
00047 "-1" \
00048 "-1" \
00049 "1" \
00050 "1" \
00051 ]
00052
00053
00054
00055
00056 namespace export prioqueue
00057
00058 ret K (type x , type y) {set x} ;
00059 }
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076 ret ::struct::prioqueue::prioqueue (type args) {
00077 variable queues
00078 variable counter
00079 variable queues_sorting
00080 variable sortopt
00081
00082 # check args
00083 if {[llength $args] > 2} {
00084 error "wrong # args: should be \"[lindex [info level 0] 0] ?-ascii|-dictionary|-integer|-real? ?name?\""
00085 }
00086 if {[llength $args] == 0} {
00087 # defaulting to integer priorities
00088 set sorting -integer
00089 } else {
00090 if {[llength $args] == 1} {
00091 if {[string match "-*" [lindex $args 0]]==1} {
00092 set sorting [lindex $args 0]
00093 } else {
00094 set sorting -integer
00095 set name [lindex $args 0]
00096 }
00097 } else {
00098 if {[llength $args] == 2} {
00099 foreach {sorting name} $args {break}
00100 }
00101 }
00102 }
00103 # check option (like lsort sorting options without -command)
00104 if {[lsearch $sortopt $sorting] == -1} {
00105 # if sortoption is unknown, but name is a sortoption we give a better error message
00106 if {[info exists name] && [lsearch $sortopt $name]!=-1} {
00107 error "wrong argument position: should be \"[lindex [info level 0] 0] ?-ascii|-dictionary|-integer|-real? ?name?\""
00108 }
00109 error "unknown sort option \"$sorting\""
00110 }
00111 # create name if not given
00112 if {![info exists name]} {
00113 incr counter
00114 set name "prioqueue${counter}"
00115 }
00116
00117 if { ![string equal [info commands ::$name] ""] } {
00118 error "command \"$name\" already exists, unable to create prioqueue"
00119 }
00120
00121 # Initialize the queue as empty
00122 set queues($name) [list ]
00123 switch -exact -- $sorting {
00124 -integer { set queues_sorting($name) 0}
00125 -real { set queues_sorting($name) 1}
00126 -ascii { set queues_sorting($name) 2}
00127 -dictionary { set queues_sorting($name) 3}
00128 }
00129
00130 # Create the command to manipulate the queue
00131 interp alias {} ::$name {} ::struct::prioqueue::QueueProc $name
00132
00133 return $name
00134 }
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150 ret ::struct::prioqueue::QueueProc (type name , optional cmd ="" , type args) {
00151 # Do minimal args checks here
00152 if { [llength [info level 0]] == 2 } {
00153 error "wrong # args: should be \"$name option ?arg arg ...?\""
00154 }
00155
00156 # Split the args into command and args components
00157 if { [string equal [info commands ::struct::prioqueue::_$cmd] ""] } {
00158 variable commands
00159 set optlist [join $commands ", "]
00160 set optlist [linsert $optlist "end-1" "or"]
00161 error "bad option \"$cmd\": must be $optlist"
00162 }
00163 return [eval [linsert $args 0 ::struct::prioqueue::_$cmd $name]]
00164 }
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176 ret ::struct::prioqueue::_clear (type name) {
00177 variable queues
00178 set queues($name) [list]
00179 return
00180 }
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193 ret ::struct::prioqueue::_destroy (type name) {
00194 variable queues
00195 variable queues_sorting
00196 unset queues($name)
00197 unset queues_sorting($name)
00198 interp alias {} ::$name {}
00199 return
00200 }
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215 ret ::struct::prioqueue::_get (type name , optional count =1) {
00216 variable queues
00217 if { $count < 1 } {
00218 error "invalid item count $count"
00219 }
00220
00221 if { $count > [llength $queues($name)] } {
00222 error "insufficient items in prioqueue to fill request"
00223 }
00224
00225 if { $count == 1 } {
00226 # Handle this as a special case, so single item gets aren't listified
00227 set item [lindex [lindex $queues($name) 0] 1]
00228 set queues($name) [lreplace [K $queues($name) [set queues($name) ""]] 0 0]
00229 return $item
00230 }
00231
00232 # Otherwise, return a list of items
00233 incr count -1
00234 set items [lrange $queues($name) 0 $count]
00235 foreach item $items {
00236 lappend result [lindex $item 1]
00237 }
00238 set items ""
00239
00240 set queues($name) [lreplace [K $queues($name) [set queues($name) ""]] 0 $count]
00241 return $result
00242 }
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256 ret ::struct::prioqueue::_peek (type name , optional count =1) {
00257 variable queues
00258 if { $count < 1 } {
00259 error "invalid item count $count"
00260 }
00261
00262 if { $count > [llength $queues($name)] } {
00263 error "insufficient items in prioqueue to fill request"
00264 }
00265
00266 if { $count == 1 } {
00267 # Handle this as a special case, so single item pops aren't listified
00268 return [lindex [lindex $queues($name) 0] 1]
00269 }
00270
00271 # Otherwise, return a list of items
00272 set index [expr {$count - 1}]
00273 foreach item [lrange $queues($name) 0 $index] {
00274 lappend result [lindex $item 1]
00275 }
00276 return $result
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291 ret ::struct::prioqueue::_peekpriority (type name , optional count =1) {
00292 variable queues
00293 if { $count < 1 } {
00294 error "invalid item count $count"
00295 }
00296
00297 if { $count > [llength $queues($name)] } {
00298 error "insufficient items in prioqueue to fill request"
00299 }
00300
00301 if { $count == 1 } {
00302 # Handle this as a special case, so single item pops aren't listified
00303 return [lindex [lindex $queues($name) 0] 0]
00304 }
00305
00306 # Otherwise, return a list of items
00307 set index [expr {$count - 1}]
00308 foreach item [lrange $queues($name) 0 $index] {
00309 lappend result [lindex $item 0]
00310 }
00311 return $result
00312 }
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326 ret ::struct::prioqueue::_put (type name , type args) {
00327 variable queues
00328 variable queues_sorting
00329 variable sortopt
00330 variable sortdir
00331
00332 if { [llength $args] == 0 || [llength $args] % 2} {
00333 error "wrong # args: should be \"$name put item prio ?item prio ...?\""
00334 }
00335
00336 # check for prio type before adding
00337 switch -exact -- $queues_sorting($name) {
00338 0 {
00339 foreach {item prio} $args {
00340 if {![string is integer -strict $prio]} {
00341 error "priority \"$prio\" is not an integer type value"
00342 }
00343 }
00344 }
00345 1 {
00346 foreach {item prio} $args {
00347 if {![string is double -strict $prio]} {
00348 error "priority \"$prio\" is not a real type value"
00349 }
00350 }
00351 }
00352 default {
00353 #no restrictions for -ascii and -dictionary
00354 }
00355 }
00356
00357 # sort by priorities
00358 set opt [lindex $sortopt $queues_sorting($name)]
00359 set dir [lindex $sortdir $queues_sorting($name)]
00360
00361 # add only if check has passed
00362 foreach {item prio} $args {
00363 set new [list $prio $item]
00364 set queues($name) [__linsertsorted [K $queues($name) [set queues($name) ""]] $new $opt $dir]
00365 }
00366 return
00367 }
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379 ret ::struct::prioqueue::_size (type name) {
00380 variable queues
00381 return [llength $queues($name)]
00382 }
00383
00384
00385
00386
00387
00388
00389
00390 ret ::struct::prioqueue::__linsertsorted (type list , type newElement , type sortopt , type sortdir) {
00391
00392 set cmpcmd __elementcompare${sortopt}
00393 set pos -1
00394 set newPrio [lindex $newElement 0]
00395
00396 # do a binary search
00397 set lower -1
00398 set upper [llength $list]
00399 set bound [expr {$upper+1}]
00400 set pivot 0
00401
00402 if {$upper > 0} {
00403 while {$lower +1 != $upper } {
00404
00405 # get the pivot element
00406 set pivot [expr {($lower + $upper) / 2}]
00407 set element [lindex $list $pivot]
00408 set prio [lindex $element 0]
00409
00410 # check
00411 set test [$cmpcmd $prio $newPrio $sortdir]
00412 if {$test == 0} {
00413 set pos $pivot
00414 set upper $pivot
00415 # now break as we need the last item
00416 break
00417 } elseif {$test > 0 } {
00418 # search lower section
00419 set upper $pivot
00420 set bound $upper
00421 set pos -1
00422 } else {
00423 # search upper section
00424 set lower $pivot
00425 set pos $bound
00426 }
00427 }
00428
00429
00430 if {$pos == -1} {
00431 # we do an insert before the pivot element
00432 set pos $pivot
00433 }
00434
00435 # loop to the last matching element to
00436 # keep a stable insertion order
00437 while {[$cmpcmd $prio $newPrio $sortdir]==0} {
00438 incr pos
00439 if {$pos > [llength $list]} {break}
00440 set element [lindex $list $pos]
00441 set prio [lindex $element 0]
00442 }
00443
00444 } else {
00445 set pos 0
00446 }
00447
00448 # do the insert without copying
00449 linsert [K $list [set list ""]] $pos $newElement
00450 }
00451
00452
00453
00454
00455
00456
00457
00458 ret ::struct::prioqueue::__elementcompare-integer (type prio , type newPrio , type sortdir) {
00459 return [expr {$prio < $newPrio ? -1*$sortdir : ($prio != $newPrio)*$sortdir}]
00460 }
00461
00462 ret ::struct::prioqueue::__elementcompare-real (type prio , type newPrio , type sortdir) {
00463 return [expr {$prio < $newPrio ? -1*$sortdir : ($prio != $newPrio)*$sortdir}]
00464 }
00465
00466 ret ::struct::prioqueue::__elementcompare-ascii (type prio , type newPrio , type sortdir) {
00467 return [expr {[string compare $prio $newPrio]*$sortdir}]
00468 }
00469
00470 ret ::struct::prioqueue::__elementcompare-dictionary (type prio , type newPrio , type sortdir) {
00471 # need to use lsort to access -dictionary sorting
00472 set tlist [lsort -increasing -dictionary [list $prio $newPrio]]
00473 set e1 [string equal [lindex $tlist 0] $prio]
00474 set e2 [string equal [lindex $tlist 1] $prio]
00475 return [expr {$e1 > $e2 ? -1*$sortdir : ($e1 != $e2)*$sortdir}]
00476 }
00477
00478
00479
00480
00481 namespace ::struct {
00482
00483 namespace import -force prioqueue::prioqueue
00484 namespace export prioqueue
00485 }
00486 package provide struct::prioqueue 1.3.1
00487