auto update edit
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.re-source edit
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.Reload a package edit
See package require.editing procedures edit
Richard Suchenwirth says: In my Tcl sandbox, I often don't even care to source a file at runtime, I typically rather- display the full proc (name, args, defaults, body) into a text widget
- edit it (maybe just insert a [puts])
- eval the text widget's content, so the proc is redefined.
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 - RSunless you're using a syntax-sensitive editor like emacs or Source Navigator, which is not quite clever enough ...
new language constructs edit
Tcl is almost unique in that you can create new language constructs if you really need them.- For an example of adding a new loop control feature, take a look at do...until in Tcl, which also doubles as a do..while construct, if you happen to prefer that logic.
- A Java-style try ... finally ... has also been implemented.
- returneval lets you return from a procedure and evaluate a script "in its place".
- Very old (or very different) language constructs have also been considered, see Goto in Tcl, Basic in Tcl, RPN in Tcl, Playing APL.
Unknown possibilities edit
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 likei = $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...
Tcl as OS glue edit
In the amazing ETLinux project [1], 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, ...No-Op Prompts edit
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
Swapping variables edit
foreach {a b} [list $b $a] break ;# RSor, maybe not a gem:
eval set a \{$b\} \; set b \{$a\} ;# Donald ArseneauThat 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 edit
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.
Whole or in pieces edit
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
The canvas is a gem edit
An analog clock in Tk is a beautifully simple and simply beautiful piece of canvas demo code.Optional arguments edit
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 FredSo 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 }
Use profile with ?# command edit
Chang LI [email protected]I defined the ?# command asproc ?# {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
LISPlike lists edit
Kevin Kenny emulates mutable lists built of cons cells with procs in Tcl and Lisp.List Comparision edit
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!
lshift for command-line argument and procedure args parsing edit
#========================================================== # 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! ;-)