TCL Training Exercise Solutions
TCL Training Exercise Solutions
Solution:
set var blue ;# a variable named var is assigned a value of blue
set blue var ;# a variable named blue is assigned to a value of var
set $var $var ;# remember how Tcl substitues, $var is substituted in both
places,
# same as set blue blue
set $blue $blue ;# The previous command set blue to the value blue. This
does the same thing
Both variables at the end have the value blue and there are still only two
variables created from this
Exercise 1 Picante:
Exercise 2 Mild:
Given a list of > 2 elements separated with :, e.g.:
set var 1:2:3:4
Print out the first and the last elements of your list.
(Use any combination of [lindex]/[lrange]/[foreach])
Solution:
One reasonable solution:
puts [lindex [split $var :] 0] [lindex [split $var :] end]
You could use lreplace here to create a list with only the first and last
elements. In this case, Ive removed all the elements in the list save for the
first and last:
puts [lreplace [split $var :] 1 end-1]
Exercise 2 Picante:
If I have a list of lists, ie:
set var [list a b [list c d]]
Or, equivalently:
set var {a b {c d}}
How can I mash all these elements to a single flat list? (ie. a b c d)
Can you write general code to do this for arbitrarily nested lists?
Solution:
I showed a solution using a side effect of eval that it undoes one list level:
proc flatten_list { l } {
while { 1 } {
set newl [eval concat $l]
if { $newl == $l } { break }
set l $newl
}
return $newl
}
Tcl 8.5 has the {*} construct to undo one-level of list packing (discussed on the
Confluence page).
An alternative solution could involve recursion (if you can follow this, youre
advanced!):
proc flatten_list { l } {
if { [llength $l] == 0 } {
return {}
} elseif { [llength $l] == 1 && [lindex $l 0] == $l } {
return $l
} else {
set ret {}
foreach it $l {
set ret [concat $ret [flatten_list $it]]
}
return $ret
}
}
Exercise 3 Mild:
Write code to strip off the C hex prefix 0x of a stringif the prefix
exists
set var1 0x10
set var2 20
The same code should work for either type of value (or any string).
Bonus (put it inside a [proc])
Solution:
I like this but there are many others:
Exercise3 Picante:
If you have two strings, use [string] functions to:
1) See if either is a substring of the other
2) Remove all spaces from both strings
3) Output all the characters that are used in only 1 string (remove all
characters that are common in both strings)
(You can use [list] and other commands for #3)
Solution:
1) I like using string match (glob pattern match) for this:
proc is_substring { a b } {
if { [string match *$a* $b] || [string match *$b* $a] } { return 1 }
return 0
}
2) Another great use for [string map]
proc remove_spaces { a ] {
return [string map { } $a]
}
3) I showed a way to use split to slurp a string into a list of characters.
proc return_non_common_chars { a b } {
set alist [split $a ]
set blist [split $b ]
set ret ;# initialized the list to return
foreach achar $alist {
if { [lsearch exact $blist $achar] == -1 } {
lappend ret $achar
}
}
foreach bchar $blist {
if { [lsearch -exact $alist $bchar] == -1 } {
lappend ret $bchar
}
}
return [lsort -unique $ret]
}
Exercise 4 Mild:
Given a list of clocks:
set clocks fclk tck testclk ioclk
Write code that outputs:
set_false_path from ck1 to ck2
For all non-matching clocks. (i.e.: dont false-path from the same clock to
itself)
Solution:
proc false_path_inter_clocks { clocks } {
foreach clki $clocks {
foreach clkj $clocks {
if { $clki != $clkj } {
puts set_false_path from $clki to $clkj
}
}
}
}
Exercise 4 Picante:
Write the following using loops:
1) A factorial (integer only) generator (compute n!)
2) Take in a number of x y coordinates, e.g.:
set coords {0 2} {4 5} {4 2}
Determine the mean and max x and y coordinates in the set given.
Hint: use [expr] (e.g. [expr {2 * 4}]) to do math in Tcl.
Solution:
1) A pretty basic idea is to loop and decrement your number:
proc factorial { n } {
set return 1
while { $n > 1 } {
set return [expr {$return * $n}]
incr n -1 ;# this is a shorthand way to decrement n by 1
}
return $return
}
2) Use args so that we can take any number of coordinates
proc crunch_coords { args } {
set maxx ""; set maxy "" ; set totx 0.0 ; set toty 0.0 ;# init outputted
values
foreach coord $args {
foreach {x y} $coord { } ;# foreach lets us do element by element
list assign
if { $maxx == "" || $maxx < $x } { set maxx $x }
if { $maxy == "" || $maxy < $y } { set maxy $y }
set toty [expr {$toty + $y}]
set totx [expr {$totx + $x}]
}
puts "maxx = $maxx"
puts "maxy = $maxy"
puts "meanx = [expr {$totx / [llength $args]}]"
puts "meany = [expr {$toty / [llength $args]}]"
}