Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
100% found this document useful (1 vote)
1K views

TCL Training Exercise Solutions

The document provides exercises and solutions for using variables, lists, strings, and other Tcl features. It includes multiple choice questions with increasing difficulty labeled as "Mild" and "Picante". Some examples include using variables to point to other variables, flattening nested lists, determining common/unique characters in strings, and inverting an array's keys and values. Procedures are provided as solutions to demonstrate techniques like regular expressions, expressions, upvar, and calling external programs.

Uploaded by

sarathi2010
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
1K views

TCL Training Exercise Solutions

The document provides exercises and solutions for using variables, lists, strings, and other Tcl features. It includes multiple choice questions with increasing difficulty labeled as "Mild" and "Picante". Some examples include using variables to point to other variables, flattening nested lists, determining common/unique characters in strings, and inverting an array's keys and values. Procedures are provided as solutions to demonstrate techniques like regular expressions, expressions, upvar, and calling external programs.

Uploaded by

sarathi2010
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 13

Exercise 1 Mild:

Follow this code:


set var blue
set blue var
set $var $var
set $blue $blue
What happens to the variables?
How many exist?

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:

If I have a variable pointing to another variable, ie:


set data val
set pointer data
How can I use the variable pointer to get the value of data?
Solution:
There are several ways you need to let the Tcl interpreter do a double
substitution.
% puts $$pointer ;# does *not* work, it outputs $data
% puts [set $pointer] ;# WORKS
% puts [set [set pointer]] ;# WORKS (remember that set with one
argument returns the value)
% puts [subst $$pointer] ;# WORKS ([subst] tells Tcl to do a second
substitution on the string

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:

proc strip_hex { string } {


return [string map {0x } $string]
}

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]}]"
}

Exercise 5 (slide session #2) Mild:


Write a procedure add_to_list that takes its first variable by name and
modifies it:
% set w a b c d e
% add_to_list w newitem
% puts $w
a b c d e newitem
Solution:
Use upvar to get a reference of a variable name in your callers scope!
proc add_to_list { lname value } {
upvar $lname loclist
lappend loclist $value
}

Exercise 5 (slide session #2) Picante:


Write a procedure invert_array that takes an array in by name and swaps
its names and values:
% array set a name1 v1 name2 v2
% puts [array get a]
name1 v1 name2 v2
% invert_array a
% puts [array get a]
v1 name1 v2 name2
Solution:
Use upvar again plus some array commands (need to overwrite the array)
proc invert_array { aname } {
upvar $aname locary
array set tmp [array get locary] ;# store array in a temporary array
array unset locary
;# we are removing the existing
array
foreach {key value} [array get tmp] {
set locary($value) $key ;# now we swap the key and values for
each pair
}
}

Exercise 6 (slide session #2) Mild:


Write a procedure that takes in a single string, ie.:
proc test { mystring } { }
Return the first digit (0-9) in the string or the empty string () if none exists
Solution:
Very simple one, just use [regexp] -- :
proc get_first_digit { string } {
if { [regexp {[0-9]} $string matchvar] } {
return $matchvar ;# this is our first digit,
}
return ;# if no digit, return the empty string
}

Exercise 6 (slide session #2) Picante:


Write the same proc as in the mild example but return all valid numbers in
the string, ie:
test my data 102 has 99 numbers
Gives:
102 99
Solution:
One method is to continually slurp the string until you hit no more numbers
we have to be a bit careful to find numbers delimited by whitespace (\s) or
the beginning (^) or end ($) of the string:
proc get_all_nums { string } {
set return {}
while { 1 } {
if { [regexp {(^|\s)([0-9]+)(\s|$)(.*)} $string all pre num post
remainder] } {
lappend return $num
set string $remainder ;# keep reducing the string until we have
no more matches
} else {
break
}
}
return $return
}

Exercise7 (slide session #2) Mild:


Write a procedure that outputs the line in the password file that matches a
given username,
proc my_entry { user } { }
To get the data, do:
set data [exec ypcat passwd]
Solution:
This is a simple example of calling an external command and capturing its
standard output:
proc my_entry { user } {
set pwdata [exec ypcat passwd]
foreach line [split $pwdata \n] {
if { [string match ${user}:* $line] } {
puts $line
}
}
}

You might also like