while

while , a built-in Tcl command, iteratively evalutes a script while the provided expression is true.

See Also

for
if
expr
foreach

Synopsis

while expression body

Description

while evaluates body as long as expression is true and then returns the empty string. Within body, continue immediately progresses to the next evaluation of expression, and break causes while to terminate immediately. Commands such as exit, return and tailcall also teminate while.

expression should almost always be enclosed in braces. If not, Tcl perfoms substitutions as usual before calling while, which then also performs substitutions while evaluating expression. This may result in double substitution, or it may result in a variable only being substituted before the first iteration when the author expected it to be substituted before every iteration. In such a case while never terminates because value of the expression never changes. Enclosing expression in braces means Tcl doesn't perform substitutions, leaving while to perform any subsitutions itself each time it evaluates the expression. The following example illustrates the difference:

set x 0
# warning: this never ends
while $x<10 {
   puts "x is $x"
   incr x
}

# this time $x is what was intended
while {$x<10} {
   puts "x is $x"
   incr x
}

The standard idio to create a loop that continues forever is:

while 1 { ... }

Any other true value may be used in place of 1.

Constraining Resource Usage

While discussing at the Tcl'ers Chat a while that would be safe in safe interpreters GPS came up with these for Tcl 8.4:

rename while real.while
proc do.while {cond body} {
    if {$cond} {uplevel 2 $body}
}
proc while {cond body} {
    set start [clock seconds]
    real.while 1 {
        do.while $cond $body
        if {[clock seconds] >= ($start + 600)} {
            return -code error {too much time used}
        }
    }
}

GPS: And yes I know that $cond should be uplevel'd...

GPS: Note that 600 seconds is somewhat crazy. I think 30 or 20 is good.

GPS: Another trick would be to prevent calls to while while within a while.

GPS: There could be a global lock (protected from set) that would just return -code error "existing while running" if the lock is active.

dkf: Why the do.while in there?

GPS: Just seemed easier to type out do.while and think of this as units rather than one big proc

GPS: Here's another without the do.while:

rename while real.while
proc while {cond body} {
    set start [clock seconds]
    real.while 1 {
        if {[uplevel [linsert $cond 0 expr]]} {
            uplevel $body
        } else return
        if {[clock seconds] > ($start + 10)} {
            return -code error "time limit exceeded in while loop"
        }
    }
}

US See also DoS