source: tol/tolsh/tolsh.vfs/lib/tcllib1.10/struct/prioqueue.tcl @ 8227d4c

1941-docker-image-to-testdevel
Last change on this file since 8227d4c was 8597867d, checked in by Jorge Pérez <josp.jorge@…>, 16 years ago

closes #976, se incluye tcllib1.10, base64 debe eliminarse del raiz de lib ya que esta incluido en tcllib

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@2556 be4846ae-f051-483a-831f-b64e7d08527f

  • Property mode set to 100644
File size: 12.7 KB
Line 
1# prioqueue.tcl --
2#
3#  Priority Queue implementation for Tcl.
4#
5# adapted from queue.tcl
6# Copyright (c) 2002,2003 Michael Schlenker
7#
8# See the file "license.terms" for information on usage and redistribution
9# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10#
11# RCS: @(#) $Id: prioqueue.tcl,v 1.9 2005/09/23 16:17:26 mic42 Exp $
12
13package require Tcl 8.2
14
15namespace eval ::struct {}
16
17namespace eval ::struct::prioqueue {
18    # The queues array holds all of the queues you've made
19    variable queues
20
21    # counter is used to give a unique name for unnamed queues
22    variable counter 0
23
24    # commands is the list of subcommands recognized by the queue
25    variable commands [list \
26        "clear" \
27        "destroy"   \
28        "get"   \
29        "peek"  \
30        "put"   \
31        "size"  \
32        "peekpriority" \
33        ]
34
35    variable sortopt [list \
36        "-integer" \
37        "-real" \
38        "-ascii" \
39        "-dictionary" \
40        ]
41
42    # this is a simple design decision, that integer and real
43    # are sorted decreasing (-1), and -ascii and -dictionary are sorted -increasing (1)
44    # the values here map to the sortopt list
45    # could be changed to something configurable.
46    variable sortdir [list \
47        "-1" \
48        "-1" \
49        "1" \
50        "1" \
51        ]
52
53
54
55    # Only export one command, the one used to instantiate a new queue
56    namespace export prioqueue
57
58    proc K {x y} {set x} ;# DKF's K combinator
59}
60
61# ::struct::prioqueue::prioqueue --
62#
63#   Create a new prioqueue with a given name; if no name is given, use
64#   prioqueueX, where X is a number.
65#
66# Arguments:
67#   sorting sorting option for lsort to use, no -command option
68#           defaults to integer
69#   name    name of the queue; if null, generate one.
70#           names may not begin with -
71#
72#
73# Results:
74#   name    name of the queue created
75
76proc ::struct::prioqueue::prioqueue {args} {
77    variable queues
78    variable counter
79    variable queues_sorting
80    variable sortopt
81
82    # check args
83    if {[llength $args] > 2} {
84        error "wrong # args: should be \"[lindex [info level 0] 0] ?-ascii|-dictionary|-integer|-real? ?name?\""
85    }
86    if {[llength $args] == 0} {
87        # defaulting to integer priorities
88        set sorting -integer
89    } else {
90        if {[llength $args] == 1} {
91            if {[string match "-*" [lindex $args 0]]==1} {
92                set sorting [lindex $args 0]
93            } else {
94                set sorting -integer
95                set name [lindex $args 0]
96            }
97        } else {
98            if {[llength $args] == 2} {
99                foreach {sorting name} $args {break}
100            }
101        }
102    }
103    # check option (like lsort sorting options without -command)
104    if {[lsearch $sortopt $sorting] == -1} {
105        # if sortoption is unknown, but name is a sortoption we give a better error message
106        if {[info exists name] && [lsearch $sortopt $name]!=-1} {
107            error "wrong argument position: should be \"[lindex [info level 0] 0] ?-ascii|-dictionary|-integer|-real? ?name?\""
108        }
109        error "unknown sort option \"$sorting\""
110    }
111    # create name if not given
112    if {![info exists name]} {
113        incr counter
114        set name "prioqueue${counter}"
115    }
116
117    if { ![string equal [info commands ::$name] ""] } {
118    error "command \"$name\" already exists, unable to create prioqueue"
119    }
120
121    # Initialize the queue as empty
122    set queues($name) [list ]
123    switch -exact -- $sorting {
124    -integer { set queues_sorting($name) 0}
125    -real    { set queues_sorting($name) 1}
126    -ascii   { set queues_sorting($name) 2}
127    -dictionary { set queues_sorting($name) 3}
128    }
129
130    # Create the command to manipulate the queue
131    interp alias {} ::$name {} ::struct::prioqueue::QueueProc $name
132
133    return $name
134}
135
136##########################
137# Private functions follow
138
139# ::struct::prioqueue::QueueProc --
140#
141#   Command that processes all queue object commands.
142#
143# Arguments:
144#   name    name of the queue object to manipulate.
145#   args    command name and args for the command
146#
147# Results:
148#   Varies based on command to perform
149
150proc ::struct::prioqueue::QueueProc {name {cmd ""} args} {
151    # Do minimal args checks here
152    if { [llength [info level 0]] == 2 } {
153    error "wrong # args: should be \"$name option ?arg arg ...?\""
154    }
155
156    # Split the args into command and args components
157    if { [string equal [info commands ::struct::prioqueue::_$cmd] ""] } {
158    variable commands
159    set optlist [join $commands ", "]
160    set optlist [linsert $optlist "end-1" "or"]
161    error "bad option \"$cmd\": must be $optlist"
162    }
163    return [eval [linsert $args 0 ::struct::prioqueue::_$cmd $name]]
164}
165
166# ::struct::prioqueue::_clear --
167#
168#   Clear a queue.
169#
170# Arguments:
171#   name    name of the queue object.
172#
173# Results:
174#   None.
175
176proc ::struct::prioqueue::_clear {name} {
177    variable queues
178    set queues($name) [list]
179    return
180}
181
182# ::struct::prioqueue::_destroy --
183#
184#   Destroy a queue object by removing it's storage space and
185#   eliminating it's proc.
186#
187# Arguments:
188#   name    name of the queue object.
189#
190# Results:
191#   None.
192
193proc ::struct::prioqueue::_destroy {name} {
194    variable queues
195    variable queues_sorting
196    unset queues($name)
197    unset queues_sorting($name)
198    interp alias {} ::$name {}
199    return
200}
201
202# ::struct::prioqueue::_get --
203#
204#   Get an item from a queue.
205#
206# Arguments:
207#   name    name of the queue object.
208#   count   number of items to get; defaults to 1
209#
210# Results:
211#   item    first count items from the queue; if there are not enough
212#           items in the queue, throws an error.
213#
214
215proc ::struct::prioqueue::_get {name {count 1}} {
216    variable queues
217    if { $count < 1 } {
218    error "invalid item count $count"
219    }
220
221    if { $count > [llength $queues($name)] } {
222    error "insufficient items in prioqueue to fill request"
223    }
224
225    if { $count == 1 } {
226    # Handle this as a special case, so single item gets aren't listified
227    set item [lindex [lindex $queues($name) 0] 1]
228    set queues($name) [lreplace [K $queues($name) [set queues($name) ""]] 0 0]
229    return $item
230    }
231
232    # Otherwise, return a list of items
233    incr count -1
234    set items [lrange $queues($name) 0 $count]
235    foreach item $items {
236        lappend result [lindex $item 1]
237    }
238    set items ""
239
240    set queues($name) [lreplace [K $queues($name) [set queues($name) ""]] 0 $count]
241    return $result
242}
243
244# ::struct::prioqueue::_peek --
245#
246#   Retrive the value of an item on the queue without removing it.
247#
248# Arguments:
249#   name    name of the queue object.
250#   count   number of items to peek; defaults to 1
251#
252# Results:
253#   items   top count items from the queue; if there are not enough items
254#       to fufill the request, throws an error.
255
256proc ::struct::prioqueue::_peek {name {count 1}} {
257    variable queues
258    if { $count < 1 } {
259    error "invalid item count $count"
260    }
261
262    if { $count > [llength $queues($name)] } {
263    error "insufficient items in prioqueue to fill request"
264    }
265
266    if { $count == 1 } {
267    # Handle this as a special case, so single item pops aren't listified
268    return [lindex [lindex $queues($name) 0] 1]
269    }
270
271    # Otherwise, return a list of items
272    set index [expr {$count - 1}]
273    foreach item [lrange $queues($name) 0 $index] {
274        lappend result [lindex $item 1]
275    }
276    return $result
277}
278
279# ::struct::prioqueue::_peekpriority --
280#
281#   Retrive the priority of an item on the queue without removing it.
282#
283# Arguments:
284#   name    name of the queue object.
285#   count   number of items to peek; defaults to 1
286#
287# Results:
288#   items   top count items from the queue; if there are not enough items
289#       to fufill the request, throws an error.
290
291proc ::struct::prioqueue::_peekpriority {name {count 1}} {
292    variable queues
293    if { $count < 1 } {
294    error "invalid item count $count"
295    }
296
297    if { $count > [llength $queues($name)] } {
298    error "insufficient items in prioqueue to fill request"
299    }
300
301    if { $count == 1 } {
302    # Handle this as a special case, so single item pops aren't listified
303    return [lindex [lindex $queues($name) 0] 0]
304    }
305
306    # Otherwise, return a list of items
307    set index [expr {$count - 1}]
308    foreach item [lrange $queues($name) 0 $index] {
309        lappend result [lindex $item 0]
310    }
311    return $result
312}
313
314
315# ::struct::prioqueue::_put --
316#
317#   Put an item into a queue.
318#
319# Arguments:
320#   name    name of the queue object
321#   args    list of the form "item1 prio1 item2 prio2 item3 prio3"
322#
323# Results:
324#   None.
325
326proc ::struct::prioqueue::_put {name args} {
327    variable queues
328    variable queues_sorting
329    variable sortopt
330    variable sortdir
331
332    if { [llength $args] == 0 || [llength $args] % 2} {
333    error "wrong # args: should be \"$name put item prio ?item prio ...?\""
334    }
335
336    # check for prio type before adding
337    switch -exact -- $queues_sorting($name) {
338        0    {
339        foreach {item prio} $args {
340        if {![string is integer -strict $prio]} {
341            error "priority \"$prio\" is not an integer type value"
342        }
343        }
344    }
345        1    {
346        foreach {item prio} $args {
347        if {![string is double -strict $prio]} {
348            error "priority \"$prio\" is not a real type value"
349        }
350        }
351    }
352        default {
353        #no restrictions for -ascii and -dictionary
354    }
355    }
356
357    # sort by priorities
358    set opt [lindex $sortopt $queues_sorting($name)]
359    set dir [lindex $sortdir $queues_sorting($name)]
360
361    # add only if check has passed
362    foreach {item prio} $args {
363        set new [list $prio $item]
364        set queues($name) [__linsertsorted [K $queues($name) [set queues($name) ""]] $new $opt $dir]
365    }
366    return
367}
368
369# ::struct::prioqueue::_size --
370#
371#   Return the number of objects on a queue.
372#
373# Arguments:
374#   name    name of the queue object.
375#
376# Results:
377#   count   number of items on the queue.
378
379proc ::struct::prioqueue::_size {name} {
380    variable queues
381    return [llength $queues($name)]
382}
383
384# ::struct::prioqueue::__linsertsorted
385#
386# Helper proc for inserting into a sorted list.
387#
388#
389
390proc ::struct::prioqueue::__linsertsorted {list newElement sortopt sortdir} {
391   
392    set cmpcmd __elementcompare${sortopt}
393    set pos -1
394    set newPrio [lindex $newElement 0]
395
396    # do a binary search
397    set lower -1
398    set upper [llength $list]
399    set bound [expr {$upper+1}]
400    set pivot 0
401   
402    if {$upper > 0} {
403        while {$lower +1 != $upper } {
404           
405           # get the pivot element
406           set pivot [expr {($lower + $upper) / 2}]
407           set element [lindex $list $pivot]
408        set prio [lindex $element 0]
409           
410           # check
411           set test [$cmpcmd $prio $newPrio $sortdir]
412           if {$test == 0} {
413                set pos $pivot
414                set upper $pivot
415                # now break as we need the last item
416                break
417           } elseif {$test > 0 } {
418                # search lower section
419                set upper $pivot
420                set bound $upper
421                set pos -1
422           } else {
423                # search upper section
424                set lower $pivot
425                set pos $bound
426           }
427        }
428       
429       
430        if {$pos == -1} {
431            # we do an insert before the pivot element
432            set pos $pivot
433        }
434       
435        # loop to the last matching element to
436        # keep a stable insertion order
437        while {[$cmpcmd $prio $newPrio $sortdir]==0} {
438        incr pos
439            if {$pos > [llength $list]} {break}
440            set element [lindex $list $pos]
441            set prio [lindex $element 0]
442        }           
443       
444    } else {
445        set pos 0
446    }
447   
448    # do the insert without copying
449    linsert [K $list [set list ""]] $pos $newElement
450}
451
452# ::struct::prioqueue::__elementcompare
453#
454# Compare helpers with the sort options.
455#
456#
457
458proc ::struct::prioqueue::__elementcompare-integer {prio newPrio sortdir} {
459    return [expr {$prio < $newPrio ? -1*$sortdir : ($prio != $newPrio)*$sortdir}]
460}
461
462proc ::struct::prioqueue::__elementcompare-real {prio newPrio sortdir} {
463    return [expr {$prio < $newPrio ? -1*$sortdir : ($prio != $newPrio)*$sortdir}]
464}
465
466proc ::struct::prioqueue::__elementcompare-ascii {prio newPrio sortdir} {
467    return [expr {[string compare $prio $newPrio]*$sortdir}]
468}
469
470proc ::struct::prioqueue::__elementcompare-dictionary {prio newPrio sortdir} {
471    # need to use lsort to access -dictionary sorting
472    set tlist [lsort -increasing -dictionary [list $prio $newPrio]]
473    set e1 [string equal [lindex $tlist 0]  $prio]
474    set e2 [string equal [lindex $tlist 1]  $prio]   
475    return [expr {$e1 > $e2 ? -1*$sortdir : ($e1 != $e2)*$sortdir}]
476}
477
478# ### ### ### ######### ######### #########
479## Ready
480
481namespace eval ::struct {
482    # Get 'prioqueue::prioqueue' into the general structure namespace.
483    namespace import -force prioqueue::prioqueue
484    namespace export prioqueue
485}
486package provide struct::prioqueue 1.3.1
Note: See TracBrowser for help on using the repository browser.