while

Difference between version 16 and 17 - Previous - Next
'''`[https://www.tcl-lang.org/man/tcl/TclCmd/while.htm%|%while]`''', a
 [Tcl 
commands%|%built-in] Tcl [command], iteratively [Many ways to eval%|%evaluates ]
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 `[expr%|%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 [Brace your expr-essions%|%enclosed inbraces].  If not, Tcl perfoms [substitution%|%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 terminatesbecause the value of the expression never changes.  Enclosing ''expression'' in
braces means Tcl doesn't perform substitutions, leaving `while` to perform anysubstitutions 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 Chatroom] 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]


<<categories>> Arts and crafts of Tcl-Tk programming |Tcl syntax help | Command | Control Structure