How do I create and iterate through a hash of hashes in TCL?

匿名 (未验证) 提交于 2019-12-03 01:34:02

问题:

How do I create and iterate through a hash of hashes in TCL?

If I have data like:

foo = {     a => {         aa => { aa1 aa2 aa3 }         ab => { ab1 ab2 ab3 }         ac => { ac1 ac2 ac3 }     }     b => {         ba => { ba1 ba2 ba3 }         bb => { bb1 bb2 bb3 }         bc => { bc1 bc2 bc3 }     }     c => {         ca => { ca1 ca2 ca3 }         cb => { cb1 cb2 cb3 }         cc => { cc1 cc2 cc3 }     } }

How do I create such a hash by inserting one leaf-node data item at a time. Something like:

lappend foo(a)(ab) "ab1"

Then how do I iterate over all data elements? like:

foreach key in foo {     foreach sub_key in foo($key) {         foreach elem in foo($key)($sub_key) {             puts "foo\($key\)\($sub_key\) is $elem"         }     } }

Edit : Unfortunately, I do not have access to the newer 'dict' construct.

回答1:

If you're not using Tcl 8.5, then you can use arrays. Note that arrays are one-dimensional, but the key is an arbitrary string that can be used to fake multi-dimensionality:

array set foo {} foreach first {a b c} {     foreach second {a b c} {         foreach third {1 2 3} {             lappend foo($first,$first$second) "$first$second$third"         }     } } parray data

and output it -- note: array keys, unlike dictionary keys, are unordered:

foreach key [array names foo] {     foreach elem $foo($key) {         puts "$key\t$elem"     } }

If you are given the keys (example 'b' and 'bc') you can get the value thusly:

set key1 b set key2 bc foreach elem $foo($key1,$key2) {puts $elem}


回答2:

Assuming you're using Tcl 8.5+, dictionaries are the way to go:

Define the dictionary is simply done:

set foo {     a {         aa { aa1 aa2 aa3 }         ab { ab1 ab2 ab3 }         ac { ac1 ac2 ac3 }     }     b {         ba { ba1 ba2 ba3 }         bb { bb1 bb2 bb3 }         bc { bc1 bc2 bc3 }     }     c {         ca { ca1 ca2 ca3 }         cb { cb1 cb2 cb3 }         cc { cc1 cc2 cc3 }     } }

Or define it programmatically:

set foo [dict create] foreach first {a b c} {     dict update foo $first subdict {         foreach second {a b c} {             foreach third {1 2 3} {                 dict lappend subdict "$first$second" "$first$second$third"             }         }     } }

And output it:

dict for {key1 subdict} $foo {     dict for {key2 list} $subdict {         foreach elem $list {             puts "$key1\t$key2\t$elem"         }     } }

edit: moved the array solution (non-dict) to a separate answer.



回答3:

If you just want to iterate through a dict (which is simply a key-value pair list) without the dict command then you can simply use the awesomeness of foreach:

set foo {   a {     aa { aa1 aa2 aa3 }     ab { ab1 ab2 ab3 }     ac { ac1 ac2 ac3 }   }   b {     ba { ba1 ba2 ba3 }     bb { bb1 bb2 bb3 }     bc { bc1 bc2 bc3 }   }   c {     ca { ca1 ca2 ca3 }     cb { cb1 cb2 cb3 }     cc { cc1 cc2 cc3 }   } }  foreach {key value} $foo {   foreach {sub_key sub_value} $value {     foreach elem $sub_value {       puts "foo\($key\)\($sub_key\) is $elem"     }   } }

on the other hand, inserting elements one at a time is painful without the dict command:

set foo {} lappend foo a {} set a_index [lsearch $foo a] set a_value_index [expr {$a_index+1}] set a_value [lindex $foo $a_value_index] lappend a_value aa {} lset foo $a_value_index $a_value # it is now too painful for me to continue :-(

fortunately you can use a pure-tcl implementation of the dict command: forward-compatible dict



回答4:

If you don't have the luxury of the Tcl 8.5 dictionary, use the keyed list commands to get the job done. You can google for one of these terms: keylget, keylset.

package require Tclx  # Create the nested structure catch {unset foo} foreach key1 {a b c} {     foreach key2 {a b c} {         catch {unset element}         foreach key3 {1 2 3} {             lappend element "$key1$key2$key3"         }         keylset foo $key1.$key1$key2 $element     } }  # Access the nested structure foreach key1 {a b c} {     foreach key2 {a b c} {         set elementList [keylget foo $key1.$key1$key2]         foreach element $elementList {             puts "foo\\$key1\\$key1$key2\\$key3 = $element"         }     } }  # # Access examples #  # Access a block of data puts "foo\\a = [keylget foo a]" # Access a nested block of data puts "foo\\b\\ba = [keylget foo b.ba]" # Access an individual element, remember that Tcl's list index is 0 based puts "foo\\c\\cb\\1 = [lindex [keylget foo c.cb] 0]"


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!