Many long time Tcl users are positively passionate about the language because of some of the wonderful things you can do with Tcl and Tk. It isn't so much the portability, or the extensions, or the internationalization (unicode) support, or even the fact that Tcl programmers are better looking than most users of those other languages. The simplicity of the Tcl language itself, coupled with the highly dynamic and introspective nature of the interpreter make wonderful things possible.
Unfortunately, all these little gems are not obvious to the first-time Tcl user. In fact, it sometimes takes quite a while to realize just how powerful Tcl can be in the "right hands." There is nothing at all wrong with using Tcl and Tk as you would any other programming language, but if you learn and use a few little tricks like these, you will be a more productive person, and you might even have a little more fun while you're at it.
This page lists a few of those "Tcl Gems" that have been developed over years and years and years and years of experienced Tcl use. Some of it is just pointers to other pages, and some is just a description of what you can do. Call this advocacy if you like, or just a collection of interesting reading, but I hope you enjoy these.
You might also want to check out Tk Gems if you're interested in some clever little tricks with different kinds of widgets.
RWT
Besides gems, sometimes a good pebble would be o.k. Have you checked the Bag of algorithms, Tcl examples, and Bag of Tk algorithms, just in case...? RS
Richard Suchenwirth shows how to set up a source file that automatically reloads itself into the interpreter every time you save it. Great development aid! Look for autoupdate on Braintwisters. There are some other cute ideas on that page, including dynamic variables and intgen which returns a new unique integer each time called.
Jeffrey Hobbs posted on comp.lang.tcl that TkCon has a command tkcon resource that he uses all the time in development to reread the script in (it is extremely handy to not have to exit and restart wish). It not only resources in the main interpreter, but reinitializes the primary slave environments. TkCon is a gem in itself. If you don't have it, get it.
See package require.
Richard Suchenwirth says: In my Tcl sandbox, I often don't even care to source a file at runtime, I typically rather
This way debugging doesn't leave traces in the original files, it's even faster than sourcing, and very intuitive. Oh, if it was a successful change, I'd also make it persistent in the source file - just ask auto_index where the proc came from ;-)
and Harald Kirsch says that one of his favorite procs is
proc learnSelection {} { set s [selection get] uplevel \#0 eval [list $s] }
DKF: Whyever not the following instead?
proc learnSelection {} { uplevel \#0 [selection get] }
Sometimes you just have to wonder about people...
proc learnSelection {} {uplevel #0 [selection get]}
is also equivalent - hashes matter only at beginning of command - RS
unless you're using a syntax-sensitive editor like emacs or Source Navigator, which is not quite clever enough ...
Tcl is almost unique in that you can create new language constructs if you really need them.
One of the few Tcl rules says that the first word of a command is the command name, and you can even legally break that, allowing for constructs like
i = $j+1 ==== set i [expr $j+1] a ( 2 ) ==== lindex $a 2 {1 .. 5} ==== {1 2 3 4 5}
without having procs i, a, 1 - that would be the known way. See Radical language modification, where this is done with the unknown command. Gadgets o.t.o.h. do "infix assignment" more orthodoxly, with "light-weight objects". Let unknown know if you want to build language modifications incrementally...
In the amazing ETLinux project [L1 ], an embedded system is developed that packs stripped-down Linux and Tcl (7.6, enhanced by mount, ifconfig, route, uuen/decode, dup, fork, kill, nice, ...) into a tiny 386SX/2 MB RAM/2 MB disk configuration. Tcl scripts do the init (first process at all), mail server, web server, ...
DKF: When I'm running tclsh or wish interactively, I have my prompt set to the usual default of % and have the following little procedure in my .tclshrc (see "The RC File" for more on tclshrc)
proc % args {uplevel 1 $args}
This lets me copy and paste whole lines of Tcl with the mouse triple-clicks without having to worry about stripping leading prompt symbols, so making it faster for me to edit sessions interactively (particularly when you want to change part way through an existing procedure definition.) Power-user type facility? You betcha! Useful? Certainly so for me. I just wish I could do this with other interactive scripting systems...
mikeH: Note that you can also set your prompt to the semi-colon and then a space ; to achieve the same goal without doing extra evals etc.. this trick also works nicely for traditional sh style shells like bash
foreach {a b} [list $b $a] break ;# RS
or, maybe not a gem:
eval set a \{$b\} \; set b \{$a\} ;# Donald Arseneau
That eval only puts you in Quoting hell, DA. For it to work, you need to do
eval [list set a $b] \; [list set b $a]
DKF: In 8.5 or with TclX, with lassign:
lassign [list $b $a] a b
Constants that shall be visible anywhere in the code must be global. Global variables need however be declared every time you use them, either with global Pi once per proc or ::Pi once per use. Or, you can proc'ify them, since procs are global too:
proc constant {name value} {proc $name {} [list return $value]} constant Pi 3.1415926535 puts "Pi = [Pi]"
wdb: My approach:
proc pi args { expr 3.14159265359 $args } proc -pi args { expr -3.14159265359 $args }
If I say, [pi / 4], the function returns correctly 0.785398163398.
A proc may be called with either a variable number of non-list arguments, which is convenient interactively, or a list of such arguments (which is good if you have the list anyway - no need for eval), if you interpret the args parameter as follows:
proc lsum {args} { if {[llength $args]==1} {set args [lindex $args 0]} expr [join $args +]+0 } ;# RS lsum 1 2 3 lsum $a_list_I_got_from_somewhere
An analog clock in Tk is a beautifully simple and simply beautiful piece of canvas demo code.
HD: Laurent Demailly has written the package opt, which looks very powerful and comprehensive, but I find the following approach very easy to implement and adequate for my needs.
Let's say I start with a proc that takes a long list of arguments, each having a default value:
proc myProc {{arg1 dflt1} {arg2 dflt2} ... {arg9 dflt9}} {
This requires the user to know the order of arguments, and it's a pain if you only want to specify a non-default argument for arg9. It would be nicer if we call myProc in this case as:
myProc -arg9 Fred
So let's rewrite myProc as:
proc myProc {args} { # Set up default values array set the_args { -arg1 dflt1 -arg2 dftl2 (etc ...) -arg9 dflt9 } # Now overwrite with user-supplied values array set the_args $args # And off we go... puts "The ninth argument is $the_args(-arg9)" }
In practice I usually have procs like this in a package:
package provide XYZ 1.1 namespace eval XYZ { variable Global array set Global {etc ...} } proc XYZ::myProc {args} { variable Global array set the_args [array get Global -*] array set the_args $args # Carry on as before }
I defined the ?# command as
proc ?# {args} { if {[info exists ::tcl_platform(debug)]} { uplevel 1 eval $args } }
US Very useful, but one round of evaluation too much. Should be:
proc ?# {args} { if {[info exists :tcl_platform(debug)]} { uplevel 1 $args } }
? means the alternative comment. It was quite useful to add profile code and you do not need to delete them when you distribute the code later, just set the tcl_platform(debug) as 0.
set tcl_platform(debug) 1 ?# package require Tclx ?# profile -commands on # your tcl code here ?# profile off report ?# parray report
Kevin Kenny emulates mutable lists built of cons cells with procs in Tcl and Lisp.
Ed Suominen (a TCL beginner as of Sept. 2001) contributes the following simple but fast proc for comparing lists. The result are two lists containing items exclusively in one argument list or the other.
# PROCEDURE: LISTCOMP # CALLS: none # RETURNS: lists of unique elements in each list # USAGE: list1, list2 are the lists to compare. # outname1, outname2 are names of lists to use as the # output of the procedure # Copyright (c) 2001 Edwin A. Suominen, http://eepatents.com # This procedure code is freeware under the terms of the no-endorsement # version of the BSD License, incorporated herein by reference, with # <OWNER> = Edwin A. Suominen, <ORGANIZATION> = author, <YEAR> = 2001. # Inventors of any inventions that employ or are based on this # procedure retain their patent rights to such inventions. proc listcomp { list1 list2 out1Name out2Name } { ### Define empty lists in case one has no unique elements set out1 {}; set out2 {} ### Test each element of each list against all elements of other list foreach {i} $list1 {j} $list2 { # First, test for unique element in list1 if { [ lsearch -exact $list2 $i ] < 0 } { lappend out1 $i } # Then test for unique element in list2 if { [ lsearch -exact $list1 $j ] < 0 } { lappend out2 $j } } ### Put results in specified lists upvar #0 $out1Name x set x $out1 upvar #0 $out2Name x set x $out2 ### END LISTCOMP return }
Setok Wouldn't it be better to sort the list first (Ologn) or build an array with the elements as keys? As far as I can tell the above can lead to On^2 time.
Michael Schlenker You're absolutly right. See Some ways to do set comparision for some much faster alternatives (more than a magnitude faster). So perhaps this proc should be moved from the Gems section, especially because it returns incorrect results!
#========================================================== # NAME : lshift # PURPOSE : shift list and return first element # AUTHOR : Richard Booth # http://www.lehigh.edu/~rvb2 # [email protected] [email protected] # --------------------------------------------------------- # ARGUMENTS : # % inputlist # List to be shifted. # RESULTS : # * Sets inputlist to 2nd to last elements of original inputlist # * Returns first element in inputlist # NOTES : # * useful for command-line arguments and procedure args processing # EXAMPLE-CALL : # # while {[llength $argv] > 0} { # set arg [lshift argv] # switch -- $arg { # -lib {set lib [lshift argv]} # -show {set show 1} # default {lappend tests $arg} # } # } # #========================================================== proc lshift {inputlist} { upvar $inputlist argv set arg [lindex $argv 0] #set argv [lrange $argv 1 end] ;# below is much faster - lreplace can make use of unshared Tcl_Obj to avoid alloc'ing the result set argv [lreplace $argv[set argv {}] 0 0] return $arg }
See also Stacks and queues. JCW - Here's a non-recursive directory walker based on it:
proc walkdirs {args} { set files {} while {[set dir [lshift args]] != ""} { foreach x [glob -nocomplain [file join $dir *]] { lappend [lindex {files args} [file isdir $x]] $x } } return $files }
RS considers this code by Lars H from Googol magnitude a true gem - it turns positive decimal integers (of any length) to bit strings:
proc dec2bin {num} { while {[regexp {[0-9]} $num]} { set num\ [string map {o0 0 o1 1 o2 2 o3 3 o4 4 i0 5 i1 6 i2 7 i3 8 i4 9 0 ""}\ [string map {0 0o 1 0i 2 1o 3 1i 4 2o 5 2i 6 3o 7 3i 8 4o 9 4i} $num]] } string map {i 1 o 0} $num }
APN Agreed. In Tcl 8.6, the %lb format specifier provides the same function.
RLH - or even the fact that Tcl programmers are better looking than most users of those other languages - Maybe I need to find another language then! ;-)