A list is good when you have a sequence. A flow script also needs to answer "what is the value for this name": the slack of each view, the number of warnings for each message id, the settings for each stage. Tcl has two keyed containers for this. The array is a variable with named elements. The dictionary is a value that holds key and value pairs, so it can be passed to a procedure and returned from one.
Reading time: about 8 minutes.
8.1 Arrays
An array element is written name(key). There is no declaration.
set slack(setup) -0.12
set slack(hold) 0.04
puts $slack(setup)
puts [array exists slack]
puts [lsort [array names slack]]
puts [array size slack]
This prints:
-0.12
1
hold setup
2
array names returns the keys, in no guaranteed order, so sort them when the order matters. array get returns a flat list of keys and values, and array set loads one. parray prints a whole array in a readable form.
array set limits {max_fanout 16 max_tran 0.30}
parray limits
This prints:
limits(max_fanout) = 16
limits(max_tran) = 0.30
An array is not a value. You cannot write puts $slack or pass $slack to a procedure. To give a procedure access, pass the array name and use upvar, as in Lesson 7, or use array get. This is the main reason many scripts prefer dictionaries.
8.2 Dictionaries
A dictionary is a list of keys and values with a set of commands that treat it as a map.
set d [dict create effort high period 2.0]
dict set d margin 0.1
puts [dict get $d period]
puts [dict exists $d margin]
puts [dict keys $d]
dict unset d margin
puts [dict size $d]
The result is 2.0, then 1, then effort period margin, then 2. A dictionary keeps its keys in the order they were added, which makes printed output predictable. Reading a key that does not exist is an error, so test with dict exists first when the key is optional.
if {[dict exists $d margin]} { puts "has margin" } else { puts "no margin" }
This prints no margin, because the key was removed. Tcl 8.7 adds dict getdef to read a key with a default, but Tcl 8.6, which most tools ship, does not have it. Use dict exists as shown.
Walking a dictionary uses dict for.
set views [dict create view1 -0.12 view2 0.30 view3 0.02]
dict for {name sl} $views {
puts [format "%-6s %6.2f" $name $sl]
}
puts [dict keys $views]
dict unset views view2
puts [dict keys $views]
This prints:
view1 -0.12
view2 0.30
view3 0.02
view1 view2 view3
view1 view3
8.3 Counting with dict incr
The commonest use is a tally. dict incr creates the key at zero if it is missing.
set counts {}
foreach id {ENCTS-423 IMPOPT-1 ENCTS-423 IMPOPT-1 ENCTS-423} {
dict incr counts $id
}
puts $counts
This prints ENCTS-423 3 IMPOPT-1 2. Lesson 9 uses this to count the messages in a log. The ids here are placeholders for the pattern. A second command, dict lappend, collects values per key.
set groups {}
dict lappend groups warn w1
dict lappend groups err e1
dict lappend groups warn w2
puts $groups
This prints warn {w1 w2} err e1.
8.4 Merging and updating
set base {effort high period 2.0}
set over {period 2.5}
puts [dict merge $base $over]
dict update base period p effort e {
set p 2.5
}
puts [dict get $base period]
dict merge gives a new dictionary in which later values replace earlier ones, which is how a default settings dictionary is combined with a user override. dict update links dictionary keys to local variables for the length of a body, and writes the changes back. This prints effort high period 2.5 and then 2.5.
8.5 Sorting by value
A dictionary has no sort command. Convert it to a list of pairs, sort, and print.
set area [dict create u_a 12 u_b 3 u_c 40]
set pairs {}
dict for {k v} $area { lappend pairs [list $k $v] }
foreach p [lsort -integer -decreasing -index 1 $pairs] {
lassign $p k v
puts [format "%-4s %3d" $k $v]
}
This prints:
u_c 40
u_a 12
u_b 3
The pattern, collect pairs and lsort -index 1, is reused in every "top N" report.
8.6 Nested dictionaries
A value can itself be a dictionary, which gives a small tree. Use the multi-key forms to reach inside.
set t {}
dict set t func max setup -0.12
dict set t func min hold 0.04
puts $t
puts [dict get $t func max setup]
This prints:
func {max {setup -0.12} min {hold 0.04}}
-0.12
The keys are func, max, setup, and the value is -0.12. A structure of this shape is a natural fit for results by view, by corner and by check type.
8.7 Transforming and filtering
Tcl 8.6 has dict map and dict filter.
puts [dict map {k v} {a 1 b 2 c 3} {expr {$v*10}}]
puts [dict filter {a 1 b 2 c 3} script {k v} {expr {$v>1}}]
This prints a 10 b 20 c 30 and then b 2 c 3. The first scales every value. The second keeps only the pairs whose value is greater than one.
8.8 Array or dictionary?
Use a dictionary when you will pass the data to a procedure, return it, or print it. Use an array when many procedures update the same table in place, or when you want the per-element trace and parray. In a new flow library, dictionaries are the usual choice because they behave like any other value.
8.9 A small Innovus example
Let us consider a per-view timing table. The Legacy reference lists timeDesign with -postRoute, -expandedViews and -outDir, and it writes one report per view. To be run at the Innovus prompt. Check the options against your release with timeDesign -help.
Legacy UI.
timeDesign -postRoute -expandedViews -outDir sample
The step produces one report for each analysis view. A script then reads the worst slack from each report and keeps them in one dictionary, keyed by view name:
set wns [dict create]
dict set wns func_ss_setup -0.12
dict set wns func_ff_hold 0.04
dict for {view value} $wns {
puts [format "%-14s %7.3f %s" $view $value [expr {$value < 0 ? "VIOLATED" : "met"}]]
}
The two numbers here are made up to show the table shape. In a real script they come from parsing the reports, which Lesson 12 shows in a pure-Tcl script. The dictionary keeps the view name and the number together, so a later step can sort the views, count the failing ones or write the table to a file.
8.10 Why an interviewer asks about this
Typical questions: "what is the difference between an array and a dictionary", "how do you count how many times each message appears" and "how do you pass an array to a procedure". The strong answers say that a dictionary is a value and an array is a variable, show dict incr for the count, and explain that an array is passed by name with upvar or converted with array get. A follow-up may ask what happens when a key is missing, and the answer is an error, so use dict exists.
Key takeaways
- An array is a variable with named elements, and
array namesgives the keys in no fixed order. - A dictionary is a value, so it can be passed, returned and printed, and it keeps keys in insertion order.
dict incranddict lappendbuild tallies and groups in one line each.- Sort a dictionary by converting it to a list of pairs, and use
dict existsbefore reading an optional key.