MS: I did not feel sure about changing SetOps, Code, 8.x directly ...Here is a different implementation of the set operations - I did not time it yet (intersect will certainly be slower). The notable differences to the previous are:
- There are no forbidden names for set elements. The previous version had problems with elements called '__SETA__', '__SETB__', '__RESULT__', '__ITEM__' or 'args'.
- The symmetric difference is redefined in terms of union and difference
- Uses 'unset -nocomplain'; is this valid for all 8.x ?
AK: Note that there is a C implementation available [1]
# --------------------------------------------- # SetOps -- Set operations for Tcl # # (C) c.l.t. community, 1999 # (C) TclWiki community, 2001 # # $Id: 1763,v 1.1 2002-06-21 03:28:48 jcw Exp $ # --------------------------------------------- # Implementation variant for tcl 8.x and beyond. # Uses namespaces and 'unset -nocomplain' # --------------------------------------------- # NOTE: [set][array names] in the {} array is faster than # [set][info locals] for local vars; it is however slower # for [info exists] or [unset] ... namespace eval ::setops { namespace export {[a-z]*} } proc ::setops::create {args} { cleanup $args } proc ::setops::cleanup {A} { # unset A to avoid collisions foreach [lindex [list $A [unset A]] 0] {.} {break} info locals } proc ::setops::union {args} { switch [llength $args] { 0 {return {}} 1 {return [lindex $args 0]} } foreach setX $args { foreach x $setX {set ($x) {}} } array names {} } proc ::setops::diff {A B} { if {[llength $A] == 0} { return {} } if {[llength $B] == 0} { return $A } # get the variable B out of the way, avoid collisions # prepare for "pure list optimisation" set ::setops::tmp [lreplace $B -1 -1 unset -nocomplain] unset B # unset A early: no local variables left foreach [lindex [list $A [unset A]] 0] {.} {break} eval $::setops::tmp info locals } proc ::setops::contains {set element} { expr {[lsearch -exact $set $element] < 0 ? 0 : 1} } proc ::setops::symdiff {A B} { union [diff $A $B] [diff $B $A] } proc ::setops::empty {set} { expr {[llength $set] == 0} } proc ::setops::intersect {args} { set res [lindex $args 0] foreach set [lrange $args 1 end] { if {[llength $res] && [llength $set]} { set res [Intersect $res $set] } else { break } } set res } proc ::setops::Intersect {A B} { # This is slower than local vars, but more robust if {[llength $B] > [llength $A]} { set res $A set A $B set B $res } set res {} foreach x $A {set ($x) {}} foreach x $B { if {[info exists ($x)]} { lappend res $x } } set res }