可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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]"