Version 43 of if

Updated 2010-06-04 18:31:55 by AMG

The reference page for if can be found at http://purl.org/tcl/home/man/tcl8.5/TclCmd/if.htm

if is a part of the Tcl core distribution.

The standard way of performing a test and executing a script depending on the result. I happen to believe that the following [if] commands are good style, and this is because the styles minimise the number of backslashes (a problem if you insist on putting open braces at the start of a line out of some belief that Tcl has to look like C) and maximise the comprehensibility and readability at a local level (where you need an otherwise clause, always mark it with else, and where the start of the then clause is not on the same line as the condition that guards it, always mark it with then - it is better to have a multi-line condition than it is to have one monster that wraps round several times.)

DKF


 # Standard simple if
 if {some condition} {
    # Note that the open-brace for the body is on the same line
    # as the if-command and condition.

    some conditionally executed script...
 }

 # Standard simple if with else clause
 if {some condition} {
    some conditionally executed script.
 } else {
    some script to execute if the condition isn't satisfied.
 }

 # Standard multi-test if
 if {first condition} {
    thing to do if the first condition succeeds
 } elseif {second condition} {
    thing to do if the first fails, but the second condition succeeds
 } else {
    what to do if none of the conditions match - this one is optional,
        but typically good practice to anticipate unexpected responses
 }

 # Handling complex (i.e. long) conditions
 if {
     this condition stretches over
     multiple lines
 } then {
     thing to do if the condition succeeds
 }

If you want to put the test and its corresponding (true) script on one line, and then have a large else spread out over several lines, you need to add a line-continuation backslash to that first line or Tcl consider the if complete and throw an error when it tries to run else as a command. Example:

 if {condition} {script} \
 else {
     longer
     script
 }

-CJU


Sample of boolean operators:

 set a 1
 set b "stuff"
 if { $a == 1 ||
      [string equal $b "nonsense" ] } { puts "found" }

This demonstrates that one can test two (or more) expressions within the if {} expr1 . Note that in the second test, a Tcl command is executed and the result is tested for true or false. In both cases, as soon as the entire if expr1 can be determined true or false, processing stops. This is sometimes called 'short circuiting'. The good news is that it permits you to write things like:

   if a particular variable exists &&
      a tcl command called using that function (variable) returns true
      do something special
   else
      we get here because the particular variable didn't exist.

The bad news, as such, is that you have to be certain that you use the right boolean connector between the parts. The && requires that all parts of the if expr1 are true. The || requires only that one of them be true.


Intuitive logical operators: You can use "and" for && and "or" for || if you use this preprocessor (accepts arguments like real if, but preprocessing works so far only on the first condition):

 proc If {cond args} {
        regsub -all " and " $cond {\&\&} cond
        regsub -all " or " $cond {||} cond
        uplevel 1 if [list $cond] $args
 } ;#RS

In Tcl, if is a function, not just a control construct. Its result is that of the then or else clause, whichever executes.

 set y [if {$x} {list a} {list b}]

$y will be "a" if $x evaluates to true, or "b" if $x evaluates to false.

expr's ?: operator does similar things.

 set y [expr {"foo"=="bar" ? "yes" : "no"}]

- SCT & RS

AMG: Be careful about using list for this purpose, because under some circumstances it must change its argument in order to produce a valid single-element list. [list a] may return "a", but [list "a b"] returns "{a b}" (it added the braces to the result). See my comment on the return page for safer ways to do this. I suggest using single-argument linsert; so basically replace "list" with "linsert" in the above code.

LV Just for completeness sake

% set x 0
0
% set y [if {$x} {list a}]
% puts $y
%

So, if returns an empty string if the body of the if does not execute.


Why do if expressions use curly brackets? Dollar signs and square brackets are processed, obviously, and everything appears to work a lot like a quoted string. What gives? Just sugar? Admittedly, a lot of coders would be scared off by quoted conditionals, but I'm curious. FW

RS: Curly braces are used to group their contents into one "word" without first-round evaluation. The contents will be evaluated unchanged by expr, which is optimized in speed, e.g. for accesses to variables. On the other hand, it does not accept all conditions that will be accepted as pure strings (operators in variables, "dot cast"...) Especially for string constants, bracing is important to prevent parsing away quote signs. You can omit braces if the condition is one "word" already (i.e. does not contain whitespace). The first three are equivalent (though the first two may be slower):

 if $foo==1     {puts YES} ;#(1)
 if "$foo == 1" {puts YES} ;#(2)
 if {$foo == 1} {puts YES} ;#(3)
 if ""==""      {puts YES} ;#(4)
 ERROR: extra characters after close-quote

So: you may do without curlies around if conditions, but they are recommended and sometimes required - not by if, but by the Tcl way.

FW: Ah, the ignorance of youth. Reading back on that is funny. I was thinking of conditionals in general, but I didn't know the general way to refer to it or realize it's the same thing as expr.


LV asks: does the evaluation of the if expr1 differ from an invocation of the expr command? If so, in what ways?

RS: I think seen from Tcl, they behave similarly - not sure about the implementation in the byte code compiler. But there is a difference in for, whose second arg is also an expr-like condition:

 for {set x $from} {[expr $from $op $to]} {...

If you want to retrieve operators from variables, you have to write an explicit, unbraced expr, which is evaluated as wanted, and returns 0 or 1, which is then evaluated by for's invocation of expr-like code...

rmax: The additional expr can be avoided. You just have to make sure, that the operator (but nothing else) gets substituted before for's second argument sees it:

 for {set x $from} "\$from $op \$to" {...

A discussion of when in an if to use == vs using some other way of comparing two variables needs to be made either here or on its own page string compare ....

DKF: They use the same parser and bytecode generator. Main difference is that expr will concatenate its arguments before parsing, but if requires a single argument.


Andreas Leitgeb pointed out on the comp.lang.tcl newsgroup (2002-08-07) that

 if 1 $cmd

is clearly faster then

 eval $cmd

See Many ways to eval for the ensuing discussion.


RJM: can anybody explain why string comparisons in if using == or eq fail when one or both strings are constants? For example

 if {$a eq abc} ...

The interpreter reports "variable references require preceding $". I'd expect to use quoting only when there are whitespaces in the string. Indeed I need quoting the constant string here.

RS: The little language of expressions (used in if, for, while, expr) is not fully the same as Tcl. The difference here is that string constants have to be explicitly grouped with "" or {}, which is not necessary in Tcl itself. Other differences are:

  • operators cannot be substituted from variables
  • functions are called like f($x,$y) instead of [f $x $y]
  • whitespace between operands and operators is not required

LV Starting in Tcl 8.5 , note that a new expr comparison becomes available for if :

 if {$pattern in [list case1 case2 case3 case4]} { ... }

gives you the functionality of lsearch -exact

while

 if {$pattern ni [list case1 case2 case3 case4]} { ... }

gives you the negative version (pattern is not in the supplied case list).


AMG: I find it interesting to compare the way different languages spell "else if". Let's see...

C else if
Fortran elseif, with spaces anywhere you please
Tcl elseif
Perl, Ada elsif
C preprocessor #elif
Python, bash elif

See also: