sets_tcl.tcl

Go to the documentation of this file.
00001 /* ----------------------------------------------------------------------*/
00002 /* */
00003 /*  sets_tcl.tcl --*/
00004 /* */
00005 /*  Definitions for the processing of sets.*/
00006 /* */
00007 /*  Copyright (c) 2004-2007 by Andreas Kupries.*/
00008 /* */
00009 /*  See the file "license.terms" for information on usage and redistribution*/
00010 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00011 /* */
00012 /*  RCS: @(#) $Id: sets_tcl.tcl,v 1.1 2007/01/21 22:15:59 andreas_kupries Exp $*/
00013 /* */
00014 /* ----------------------------------------------------------------------*/
00015 
00016 package require Tcl 8.0
00017 
00018 namespace ::struct:: {
00019     # Only =  export one command, the one used to instantiate a new tree
00020     namespace export _tcl = 
00021 }
00022 
00023 /* */
00024 /*  Public functions*/
00025 
00026 /*  ::struct::set::set --*/
00027 /* */
00028 /*  Command that access all set commands.*/
00029 /* */
00030 /*  Arguments:*/
00031 /*  cmd Name of the subcommand to dispatch to.*/
00032 /*  args    Arguments for the subcommand.*/
00033 /* */
00034 /*  Results:*/
00035 /*  Whatever the result of the subcommand is.*/
00036 
00037 ret  ::struct::set::set_tcl (type cmd , type args) {
00038     # Do minimal args checks here
00039     if { [llength [info level 0]] == 1 } {
00040     return -code error "wrong # args: should be \"$cmd ?arg arg ...?\""
00041     }
00042     ::set sub S_$cmd
00043     if { [llength [info commands ::struct::set::$sub]] == 0 } {
00044     ::set optlist [info commands ::struct::set::S_*]
00045     ::set xlist {}
00046     foreach p $optlist {
00047         lappend xlist [string range $p 17 end]
00048     }
00049     return -code error \
00050         "bad option \"$cmd\": must be [linsert [join [lsort $xlist] ", "] "end-1" "or"]"
00051     }
00052     return [uplevel 1 [linsert $args 0 ::struct::set::$sub]]
00053 }
00054 
00055 /* */
00056 /*  Implementations of the functionality.*/
00057 /* */
00058 
00059 /*  ::struct::set::S_empty --*/
00060 /* */
00061 /*        Determines emptiness of the set*/
00062 /* */
00063 /*  Parameters:*/
00064 /*        set   -- The set to check for emptiness.*/
00065 /* */
00066 /*  Results:*/
00067 /*        A boolean value. True indicates that the set is empty.*/
00068 /* */
00069 /*  Side effects:*/
00070 /*        None.*/
00071 /* */
00072 /*  Notes:*/
00073 
00074 ret  ::struct::set::S_empty (type set) {
00075     return [expr {[llength $set] == 0}]
00076 }
00077 
00078 /*  ::struct::set::S_size --*/
00079 /* */
00080 /*  Computes the cardinality of the set.*/
00081 /* */
00082 /*  Parameters:*/
00083 /*  set -- The set to inspect.*/
00084 /* */
00085 /*  Results:*/
00086 /*        An integer greater than or equal to zero.*/
00087 /* */
00088 /*  Side effects:*/
00089 /*        None.*/
00090 
00091 ret  ::struct::set::S_size (type set) {
00092     return [llength [Cleanup $set]]
00093 }
00094 
00095 /*  ::struct::set::S_contains --*/
00096 /* */
00097 /*  Determines if the item is in the set.*/
00098 /* */
00099 /*  Parameters:*/
00100 /*  set -- The set to inspect.*/
00101 /*  item    -- The element to look for.*/
00102 /* */
00103 /*  Results:*/
00104 /*  A boolean value. True indicates that the element is present.*/
00105 /* */
00106 /*  Side effects:*/
00107 /*        None.*/
00108 
00109 ret  ::struct::set::S_contains (type set , type item) {
00110     return [expr {[lsearch -exact $set $item] >= 0}]
00111 }
00112 
00113 /*  ::struct::set::S_union --*/
00114 /* */
00115 /*  Computes the union of the arguments.*/
00116 /* */
00117 /*  Parameters:*/
00118 /*  args    -- List of sets to unify.*/
00119 /* */
00120 /*  Results:*/
00121 /*  The union of the arguments.*/
00122 /* */
00123 /*  Side effects:*/
00124 /*        None.*/
00125 
00126 ret  ::struct::set::S_union (type args) {
00127     switch -exact -- [llength $args] {
00128     0 {return {}}
00129     1 {return [lindex $args 0]}
00130     }
00131     foreach setX $args {
00132     foreach x $setX {::set ($x) {}}
00133     }
00134     return [array names {}]
00135 }
00136 
00137 
00138 /*  ::struct::set::S_intersect --*/
00139 /* */
00140 /*  Computes the intersection of the arguments.*/
00141 /* */
00142 /*  Parameters:*/
00143 /*  args    -- List of sets to intersect.*/
00144 /* */
00145 /*  Results:*/
00146 /*  The intersection of the arguments*/
00147 /* */
00148 /*  Side effects:*/
00149 /*        None.*/
00150 
00151 ret  ::struct::set::S_intersect (type args) {
00152     switch -exact -- [llength $args] {
00153     0 {return {}}
00154     1 {return [lindex $args 0]}
00155     }
00156     ::set res [lindex $args 0]
00157     foreach set [lrange $args 1 end] {
00158     if {[llength $res] && [llength $set]} {
00159         ::set res [Intersect $res $set]
00160     } else {
00161         # Squash 'res'. Otherwise we get the wrong result if res
00162         # is not empty, but 'set' is.
00163         ::set res {}
00164         break
00165     }
00166     }
00167     return $res
00168 }
00169 
00170 ret  ::struct::set::Intersect (type A , type B) {
00171     if {[llength $A] == 0} {return {}}
00172     if {[llength $B] == 0} {return {}}
00173 
00174     # This is slower than local vars, but more robust
00175     if {[llength $B] > [llength $A]} {
00176     ::set res $A
00177     ::set A $B
00178     ::set B $res
00179     }
00180     ::set res {}
00181     foreach x $A {::set ($x) {}}
00182     foreach x $B {
00183     if {[info exists ($x)]} {
00184         lappend res $x
00185     }
00186     }
00187     return $res
00188 }
00189 
00190 /*  ::struct::set::S_difference --*/
00191 /* */
00192 /*  Compute difference of two sets.*/
00193 /* */
00194 /*  Parameters:*/
00195 /*  A, B    -- Sets to compute the difference for.*/
00196 /* */
00197 /*  Results:*/
00198 /*  A - B*/
00199 /* */
00200 /*  Side effects:*/
00201 /*        None.*/
00202 
00203 ret  ::struct::set::S_difference (type A , type B) {
00204     if {[llength $A] == 0} {return {}}
00205     if {[llength $B] == 0} {return $A}
00206 
00207     array set tmp {}
00208     foreach x $A {::set tmp($x) .}
00209     foreach x $B {catch {unset tmp($x)}}
00210     return [array names tmp]
00211 }
00212 
00213 if {0} {
00214     /*  Tcllib SF Bug 1002143. We cannot use the implementation below.*/
00215     /*  It will treat set elements containing '(' and ')' as array*/
00216     /*  elements, and this screws up the storage of elements as the name*/
00217     /*  of local vars something fierce. No way around this. Disabling*/
00218     /*  this code and always using the other implementation (s.a.) is*/
00219     /*  the only possible fix.*/
00220 
00221     if {[package vcompare [package provide Tcl] 8.4] < 0} {
00222     /*  Tcl 8.[23]. Use explicit array to perform the operation.*/
00223     } else {
00224     /*  Tcl 8.4+, has 'unset -nocomplain'*/
00225 
00226     ret  ::struct::set::S_difference (type A , type B) {
00227         if {[llength $A] == 0} {return {}}
00228         if {[llength $B] == 0} {return $A}
00229 
00230         # Get the variable B out of the way, avoid collisions
00231         # prepare for "pure list optimization"
00232         ::set ::struct::set::tmp [lreplace $B -1 -1 unset -nocomplain]
00233         unset B
00234 
00235         # unset A early: no local variables left
00236         foreach [lindex [list $A [unset A]] 0] {.} {break}
00237 
00238         eval $::struct::set::tmp
00239         return [info locals]
00240     }
00241     }
00242 }
00243 
00244 /*  ::struct::set::S_symdiff --*/
00245 /* */
00246 /*  Compute symmetric difference of two sets.*/
00247 /* */
00248 /*  Parameters:*/
00249 /*  A, B    -- The sets to compute the s.difference for.*/
00250 /* */
00251 /*  Results:*/
00252 /*  The symmetric difference of the two input sets.*/
00253 /* */
00254 /*  Side effects:*/
00255 /*        None.*/
00256 
00257 ret  ::struct::set::S_symdiff (type A , type B) {
00258     # symdiff == (A-B) + (B-A) == (A+B)-(A*B)
00259     if {[llength $A] == 0} {return $B}
00260     if {[llength $B] == 0} {return $A}
00261     return [S_union \
00262         [S_difference $A $B] \
00263         [S_difference $B $A]]
00264 }
00265 
00266 /*  ::struct::set::S_intersect3 --*/
00267 /* */
00268 /*  Return intersection and differences for two sets.*/
00269 /* */
00270 /*  Parameters:*/
00271 /*  A, B    -- The sets to inspect.*/
00272 /* */
00273 /*  Results:*/
00274 /*  List containing A*B, A-B, and B-A*/
00275 /* */
00276 /*  Side effects:*/
00277 /*        None.*/
00278 
00279 ret  ::struct::set::S_intersect3 (type A , type B) {
00280     return [list \
00281         [S_intersect $A $B] \
00282         [S_difference $A $B] \
00283         [S_difference $B $A]]
00284 }
00285 
00286 /*  ::struct::set::S_equal --*/
00287 /* */
00288 /*  Compares two sets for equality.*/
00289 /* */
00290 /*  Parameters:*/
00291 /*  a   First set to compare.*/
00292 /*  b   Second set to compare.*/
00293 /* */
00294 /*  Results:*/
00295 /*  A boolean. True if the lists are equal.*/
00296 /* */
00297 /*  Side effects:*/
00298 /*        None.*/
00299 
00300 ret  ::struct::set::S_equal (type A , type B) {
00301     ::set A [Cleanup $A]
00302     ::set B [Cleanup $B]
00303 
00304     # Equal if of same cardinality and difference is empty.
00305 
00306     if {[::llength $A] != [::llength $B]} {return 0}
00307     return [expr {[llength [S_difference $A $B]] == 0}]
00308 }
00309 
00310 
00311 ret  ::struct::set::Cleanup (type A) {
00312     # unset A to avoid collisions
00313     if {[llength $A] < 2} {return $A}
00314     foreach [lindex [list $A [unset A]] 0] {.} {break}
00315     return [info locals]
00316 }
00317 
00318 /*  ::struct::set::S_include --*/
00319 /* */
00320 /*  Add an element to a set.*/
00321 /* */
00322 /*  Parameters:*/
00323 /*  Avar    -- Reference to the set variable to extend.*/
00324 /*  element -- The item to add to the set.*/
00325 /* */
00326 /*  Results:*/
00327 /*  None.*/
00328 /* */
00329 /*  Side effects:*/
00330 /*        The set in the variable referenced by Avar is extended*/
00331 /*  by the element (if the element was not already present).*/
00332 
00333 ret  ::struct::set::S_include (type Avar , type element) {
00334     # Avar = Avar + {element}
00335     upvar 1 $Avar A
00336     if {![info exists A] || ![S_contains $A $element]} {
00337     lappend A $element
00338     }
00339     return
00340 }
00341 
00342 /*  ::struct::set::S_exclude --*/
00343 /* */
00344 /*  Remove an element from a set.*/
00345 /* */
00346 /*  Parameters:*/
00347 /*  Avar    -- Reference to the set variable to shrink.*/
00348 /*  element -- The item to remove from the set.*/
00349 /* */
00350 /*  Results:*/
00351 /*  None.*/
00352 /* */
00353 /*  Side effects:*/
00354 /*        The set in the variable referenced by Avar is shrunk,*/
00355 /*  the element remove (if the element was actually present).*/
00356 
00357 ret  ::struct::set::S_exclude (type Avar , type element) {
00358     # Avar = Avar + {element}
00359     upvar 1 $Avar A
00360     while {[::set pos [lsearch -exact $A $element]] >= 0} {
00361     ::set A [lreplace [K $A [::set A {}]] $pos $pos]
00362     }
00363     return
00364 }
00365 
00366 /*  ::struct::set::S_add --*/
00367 /* */
00368 /*  Add a set to a set. Similar to 'union', but the first argument*/
00369 /*  is a variable.*/
00370 /* */
00371 /*  Parameters:*/
00372 /*  Avar    -- Reference to the set variable to extend.*/
00373 /*  B   -- The set to add to the set in Avar.*/
00374 /* */
00375 /*  Results:*/
00376 /*  None.*/
00377 /* */
00378 /*  Side effects:*/
00379 /*        The set in the variable referenced by Avar is extended*/
00380 /*  by all the elements in B.*/
00381 
00382 ret  ::struct::set::S_add (type Avar , type B) {
00383     # Avar = Avar + B
00384     upvar 1 $Avar A
00385     ::set A [S_union [K $A [::set A {}]] $B]
00386     return
00387 }
00388 
00389 /*  ::struct::set::S_subtract --*/
00390 /* */
00391 /*  Remove a set from a set. Similar to 'difference', but the first argument*/
00392 /*  is a variable.*/
00393 /* */
00394 /*  Parameters:*/
00395 /*  Avar    -- Reference to the set variable to shrink.*/
00396 /*  B   -- The set to remove from the set in Avar.*/
00397 /* */
00398 /*  Results:*/
00399 /*  None.*/
00400 /* */
00401 /*  Side effects:*/
00402 /*        The set in the variable referenced by Avar is shrunk,*/
00403 /*  all elements of B are removed.*/
00404 
00405 ret  ::struct::set::S_subtract (type Avar , type B) {
00406     # Avar = Avar - B
00407     upvar 1 $Avar A
00408     ::set A [S_difference [K $A [::set A {}]] $B]
00409     return
00410 }
00411 
00412 /*  ::struct::set::S_subsetof --*/
00413 /* */
00414 /*  A predicate checking if the first set is a subset*/
00415 /*  or equal to the second set.*/
00416 /* */
00417 /*  Parameters:*/
00418 /*  A   -- The possible subset.*/
00419 /*  B   -- The set to compare to.*/
00420 /* */
00421 /*  Results:*/
00422 /*  A boolean value, true if A is subset of or equal to B*/
00423 /* */
00424 /*  Side effects:*/
00425 /*        None.*/
00426 
00427 ret  ::struct::set::S_subsetof (type A , type B) {
00428     # A subset|== B <=> (A == A*B)
00429     return [S_equal $A [S_intersect $A $B]]
00430 }
00431 
00432 /*  ::struct::set::K --*/
00433 /*  Performance helper command.*/
00434 
00435 ret  ::struct::set::K (type x , type y) {::set x}
00436 
00437 /*  ### ### ### ######### ######### #########*/
00438 /*  Ready*/
00439 
00440 namespace ::struct {
00441     /*  Put 'set::set' into the general structure namespace*/
00442     /*  for pickup by the main management.*/
00443 
00444     namespace import -force ::set = _tcl
00445 }
00446 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1