A Hash Table in Tcl

George Peter Staplin Nov 28, 2008 - Hash tables are generally used to associate strings or values with other values. This is a simple implementation of such a thing in Tcl. Tcl's array and dict are similar, though implemented in C.


 #By George Peter Staplin
 #
 #This is a simple demonstration of a hash table for Tcl implemented in Tcl.
 #We use a linear list of sublists.
 #
 #In Tcl lists are like C arrays, so this is quite efficient.
 #
 #You can store a hash table in a hash table, so it's less restricted than [array].
 #This might be beneficial to those that aren't using [dict] or [objstructure].
 
 
 proc hash-create {} {
     set numbuckets 500
     return [lrepeat $numbuckets [list]]
 }
 
 
 proc hash str {
     set n 0
     
     foreach c [split $str ""] {
         #This hash algorithm is like Tcl's core hash.
         set n [expr {$n + ($n << 3) + [scan $c %c]}]
     }
     
     return $n
 }
 
 proc hash-set {bucketsvar key value} {
     upvar 1 $bucketsvar buckets
 
     set numbuckets [llength $buckets]
     set hash [hash $key]
     set offset [expr {$hash % $numbuckets}]
     set sublist [lindex $buckets $offset]
     set existing [lsearch -exact -index 0 $sublist $key]
 
     if {$existing < 0} {
         #This is a new key that hashed to this bucket.
         lappend sublist [list $key $value]
     } else {
         #This is a key that is the same as a previous key.
         #Replace the bucket's sublist item.
         set sublist [lreplace $sublist $existing $existing [list $key $value]]
     }
 
     lset buckets $offset $sublist
 }
 
 
 proc hash-get {buckets key valuevar} {
     upvar 1 $valuevar value
 
     set numbuckets [llength $buckets]
     set hash [hash $key]
     set offset [expr {$hash % $numbuckets}]
     set sublist [lindex $buckets $offset]
     
     set existing [lsearch -exact -index 0 $sublist $key]
 
     if {$existing < 0} {
         #not found
         return 0
     }
 
     set value [lindex $sublist $existing 1]
     
     #found
     return 1
 }
 
 proc main {} {
     set h [hash-create]
 
     set key1 "Hello World!"
     set key2 "Goodbye World!"
 
     puts keyset1:[time {
         hash-set h $key1 123
     }]
     puts keyset2:[time {
         hash-set h $key2 456
     }]
     
     if {[hash-get $h $key1 value]} {
         puts "Found $value associated with: $key1"
     }
 
     if {[hash-get $h $key2 value]} {
         puts "Found $value associated with $key2"
     }
 
     if {[hash-get $h "invalid key" value]} {
         puts "Invalid key returned a value?"
     }
 
     hash-set h $key1 789
     
     if {[hash-get $h $key1 value]} {
         puts "updated $key1 value is: $value"
     }
 }
 main