'''`[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