Documentation for the Tcl switch command can be found at http://www.purl.org/tcl/home/man/tcl8.4/TclCmd/switch.htm http://www.purl.org/tcl/home/man/tcl8.5/TclCmd/switch.htm History corner: First announced by [JO] for Tcl 7.0 in [http://groups.google.com/groups?q=group:comp.lang.tcl+author:ousterhout&start=800&hl=de&lr=&newwindow=1&scoring=d&selm=1t8s4g%24kfs%40agate.berkeley.edu&rnum=840] ---- [RS] Make sure you always write "the switch to end all switches", "--", if there is the slightest possibility that faulty or unexpected data (i.e., beginning with a dash) may occur - they raise a data-driven syntax error which does not make a good impression on end-users. switch -- $foo {...} ;# robust for all values of foo ---- In the form of the switch command where all patterns and bodies are in the last argument, this argument is technically a string which will be parsed by Tcl as a list. The rules for how a string is parsed as a list can be found on the [lindex] manual page [http://www.purl.org/tcl/home/man/tcl8.4/TclCmd/lindex.htm]. The difference here between a whitespace-delimited substring and a list element parsed from a string is usually negligible, but one must sometimes take them into account to get the right amount of quoting in place. Regexp patterns are particularly easy to overquote. ---- Coding style for switch is tricky. Recently on comp.lang.tcl there was an exchange about getting switch to work right. The user had originally typed: switch $::errorCode { $NOTFOUND { puts "page not found " } default { puts "Program encountered code $::errorCode" break } } and was frustrated because the application kept taking the default branch. A well known Tcl pundit suggested: switch $::errorCode " $NOTFOUND { puts "page not found " } default { puts "Program encountered code $::errorCode" break } " so that Tcl had a chance to substitue the value of $NOTFOUND. However, the follow up, by [Ken Jones] said it best: you don't want that (unless you're doing some very, very, very tricky stuff). Quoting switch's pattern/action block with "" allows substitutions within the entire argument. So, yes you get the $URL_NOT_FOUND variable substituted. But you also get the contents of the actions substituted before switch even gets called. [lexfiend] - I think the following example illustrates Ken's point better: # GOTCHA - You're dead no matter what you do set TriggerBomb 0 set DefuseBomb 1 set WaitAMinuteWhileIThinkThisThrough 2 set Action $WaitAMinuteWhileIThinkThisThrough interp alias {} SendToBomb {} puts switch $Action " $TriggerBomb { set result [SendToBomb boom] } $DefuseBomb { set result [SendToBomb shutdown] } " Instead, we want the "alternate" syntax of switch, where you provide each pattern and its corresponding action as separate arguments to switch, rather than as a monolithic pattern/action quoted argument. Here's a correctly working example of the above code: set NOTFOUND "value" set errorCode "nonsense" switch -- $::errorCode \ $NOTFOUND { puts "page not found" } \ default { puts "Program encountered code $::errorCode" break } [KBK] - I dislike both of these. I tend to think of a switch as a selection among fixed alternatives, and substitution in the cases confuses me. I'd have just written: if { [string equal $::errorcode $NOTFOUND] } { puts "page not found" } else { puts "Program encountered code $::errorCode" break } [RHS] - I find that, when I need substitution for switch values, formatting it like the following looks cleaner: switch -- $::errorCode $NOTFOUND { puts "page not found" } $value1 { puts "blah" } default { puts "Program encountered code $::errorCode" break } ---- The above sample was, of course, intended to be brief, and didn't show the other several dozen switch cases . For a single comparison, I agree with KBK. But if I have more than one or two of these types of comparisons, then I know '''I''' [LV] prefer a switch. ---- With '''switch -regexp''', you can specify abbreviations of a flag to be switched on like this: switch -regexp -- $variable { -h(e(lp?)?)? {...} -h|-he|-hel|-help|-ayuda {...} -\\? { ... } ;$ Match -? flag "(?x) - | -h | -help" {do stuff} ;# extended RE syntax } without the -regexp, you would write: switch -- $variable { -h - -he - -hel - -help { ... } } ---- [LV] I really find using - as a seperator between switch case alternatives to be counter-intuitive; normally one uses | for that sort of thing . [glennj]: It might be clearer if more whitespace is used: switch -- $variable { -h - -he - -hel - -help { ... } } ---- [SB], 2003-05-21: When I use switch -regexp, Tcl already use regexp once to enter the proper clause. How can the match pattern be accessed from within the clause, if possible, and is switch -regexp able to use () match variables? I think perl store the regexp matches in global variables $1 .. $9 (The following example is not valid, only for illustrating question) switch -regexp -- $somevar { {^(\S+)\s+(\S+)} {puts "You hit matches $1 and $2"} {^(\d+)\s+(\d+)} {puts "You hit number matches $1 and $2"} } [KBK] - You can't. However, that form of [[switch]] is no slower than the corresponding [[if]]...[[elseif]], so you can do: if { [regexp {^(\S+)\s+(\S+)$} $somevar -> part1 part2] } { puts "You hit matches $part1 and $part2" } elseif { [regexp {^(\d+)\s+(\d+)} -> part1 part2] } { puts "You hit number matches $part1 and $part2" } [hkoba], 2005-03-18: Hmm, how about [switch-regexp]? ---- How's this for a silly compromise. Nicely aligns the dispatch conditions and removes the confusing barrage of backslashes. if {0} { } elseif {$state == $INIT_STATE} { puts init } elseif {$state == $CLEANING_STATE} { puts cleaning } elseif {$state == $DONE_STATE} { puts done } ---- The silliness continues... this actually works though I'm not sure of the side effects. proc dispatch {thevar args} { set pass1 [uplevel subst $args] set pass2 "switch \$$thevar \{ $pass1 \}" uplevel $pass2 } set INIT_STATE "init" set CLEANING_STATE "cleaning" set DONE_STATE "done" set state $INIT_STATE dispatch state { $INIT_STATE { puts init set nextstate $DONE_STATE } $CLEANING_STATE { puts cleaning } $DONE_STATE { puts done } default { } } puts "next state $nextstate" ---- See also [ranged switch] ---- [LES]: So, if it is so good a practice ALWAYS to call switch as "switch --" (the switch to end all switches), why can't the core code already cope with that? [MG] That's not really specific to switch - it's really good practice with ''all'' commands that take ''-something'' args, and show the end of those with '--', to include the --. And I think the reason the core can't handle is that you may actually want the behaviour that occurs when you don't give --, some times. [LV] because technically the rule isn't "ALWAYS call switch as switch --". The rule is "ALWAYS call switch with -- after the last of the switch flags". If you are using switch without a flag, then it would be "switch --". Think of code like this: set arg "something entered by the user" switch $arg { --first_thing { do something } --second_thing { do something else } default {do the last thing } } Now, if the user happens to type in "--first_thing", the program raises the error: bad option "--first_thing": must be -exact, -glob, -regexp, or -- because there was no "--" between the switch and the variable being examined. Not only that, but if the user happens to type in "-exact", the error raised is: wrong # args: should be "switch ?switches? string pattern body ... ?default body?" because of the missing "--" . Two potential error situations - and the core switch command can't handle either, because by the time it is called, any information about where the first argument is already gone. [RS] The need for -- seems to exist only for ''switch''. Other commands don't get confused by parameters that look like switches but aren't: % lsearch -foo -bar -1 Then again, [regexp] has the same need for --: % regexp -foo -bar bad switch "-foo": must be -all, -about, -indices, -inline, -expanded, -line, -linestop, -lineanchor, -nocase, -start, or -- ---- [RS] 2005-05-30: Unlike in [C], [Tcl]'s [switch] returns its last evaluated result, so it can be used as a function. Given this simple identity proc is x {set x} we can code the following "digit classifier" quite elegantly: foreach num {0 1 2 3 4 5 6 7 8 9} { set type [switch -- $num { 1 - 9 {is odd} 2 - 3 - 5 - 7 {is prime} 0 - 4 - 6 - 8 {is even} }] puts "$num is $type" } which displays 0 is even 1 is odd 2 is prime 3 is prime 4 is even 5 is prime 6 is even 7 is prime 8 is even 9 is odd ---- [LV] Can someone help me with switch? I have what seems like a simple bit of code, but it doesn't work as I expected. So I need help adjusting my expectation (and my code!). $ cat ts.tcl #! /usr/tcl84/bin/tclsh set ::expert "no" set ::usage "USAGE: $argv0 arg1 arg2" set ::val "XYZ" foreach i $::argv { switch -exact -- $i in { ! { if {$::expert == "no"} { set ::expert "yes" } else { puts $::usage exit 1 } } 1 { set $::val 1 } default { puts "** Too many values specified **" puts "You will be prompted for the information **" set ::expert "no" } } } puts $::expert puts $::val $ ts.tcl 1 2 ! no XYZ I was expecting to see no ** Too many values specified ** You will be prompted for the information ** 1 as a result, since I supplied a 2 and a !. What am I missing in this? [RHS] You have an '''in''' there that you shouldn't: switch -exact -- $i in { ^^ ---- '''Compilation Notes''' [DKF]: In Tcl 8.5, the '''switch''' command is bytecompiled if reasonably possible. The "reasonably possible" part means that you do have to be doing either '''-exact''' or '''-glob''' matching, and the bodies of the arms have to be compilable (as usual). With '''-regexp''' matching, that always goes to the "interpreted" version (though by compensation, there are some other new goodies (see -matchvar and -indexvar, for instance) available). In most cases, the switch is compiled to (effectively) a sequence of [if]s. However, in some special cases (i.e. '''-exact''' matching of constant terms) a jump table is built instead, which is considerably more efficient, especially for terms other than the first one. ---- [[[Category Command]|[Category Control Structure]|[Tcl syntax help]|[Arts and crafts of Tcl-Tk programming]]]