Ticket UUID: | 550789 | |||
Title: | [break] should take an 'after'-script | |||
Type: | RFE | Version: | None | |
Submitter: | nobody | Created on: | 2002-04-30 21:28:32 | |
Subsystem: | 16. Commands A-H | Assigned To: | dgp | |
Priority: | 3 Low | Severity: | ||
Status: | Open | Last Modified: | 2003-02-25 16:32:31 | |
Resolution: | None | Closed By: | ||
Closed on: | ||||
Description: |
It would be very convenient if one could supply to break a script that will be evaluated after the loop command that the [break] is breaking has ended, but before the first command after it. This could be used to break out of nested loops: foreach i {a b c d} { foreach j {a b c d} { set foo [bar $i $j] if {$foo<3} then { break {break} } # Some more processing } # break in break-script would be evaluated here incr sum $foo ; # Some still more processing } or to break out of an inner loop and immediately continue to the next iteration of an outer: for {set i 0} {$i<$i_max} {incr i} { for {set j 0} {$j<$j_max} {incr j} { if {[try_it $i $j]} then { set T($i) $j break {continue} } } # continue in break-script would be # evaluated here puts stdout "No solution found for i=$i." } To break out of three nested loops, one would similarly give the command break {break {break}} In want of this feature, I have lately found myself making very frequent use of auxiliary variables, so that the first example would instead have been set breaking 0 foreach i {a b c d} { foreach j {a b c d} { set foo [bar $i $j] if {$foo<3} then { set breaking 1 break } # Some more processing } if {$breaking} then {break} incr sum $foo ; # Some still more processing } which is much less elegant (and no doubt slower). Making special procedures for loops that I need to break out of (so that I can use [return] instead) helps in some cases, but is not always appropriate. As far as the implementation is concerned, this does not seem particularly complicated. Since no more than one [break] can be going on at any single time, the interpreter never needs to keep track of more than one break-script. In principle, all the [break] command would have to do would be to set some variable (perhaps a global variable "breakScript", to go with "errorInfo" and "errorCode"?) to the break-script. Then the looping commands need, when they catch a TCL_BREAK return, only look at this variable to see if there is a special script to evaluate or not. Even more streamlined would be to have a [break] with a script argument return some new code, say, TCL_BREAK_SCRIPT instead of TCL_BREAK. Some notes: 1. Since break-scripts can make TCL_BREAK and TCL_CONTINUE returns, they appearently return a result. This can be used to have [for], [foreach], and [while] return a non-empty result: set i_pos\ [foreach i $some_list { if {$A($i)>0} then {break {set i}} }] will set $i_pos to the first $i for which $A($i)>0, or to an empty string if there was no such $i. In the case of [for], this would not add any extra functionality since a separate [set i] commad after the [for] would have pretty much the same effect, but in the case of [foreach] it allows one to distinguish between not finding anything and finding something on the last iteration. 2. Procedure bodies and the [catch] command also catch TCL_BREAK returns; how should they react to break- scripts then? In the case of procedure bodies, evaluating [break] is normally an error, so there is no need to react to the break-script at all. In the case of catch there is similarly no need to evaluate the break- script, as the code [catch]ing might want to inspect it (just as with $errorCode); this is why it would make sense to put the script in a variable that is accessible from Tcl. 3. The mechanism proposed above have many similarities to the TCL_EVAL return code suggested in TIP #90. It is probably best treated in conjuction with that. /Lars Hellström | |||
User Comments: |
nobody added on 2002-05-26 01:28:12:
Logged In: NO At 2002-05-10 14:56, dgp wrote: > The particular applications outlined in the proposal are > worthwhile and interesting, but Tcl already provides the > ability to define new looping commands and new completion > codes that can implement those ideas. Whereas this is quite true, it is also the case that the elegant way of defining these commands is far from obvious, and the reply would have been much more enlightning if it had contained an example. As it were, I stumbled upon *the* (? -- there could be others ...) elegant solution and could share it with the world on the Wiki (see http://mini.net/tcl/return), but there was a fair amount of luck in that. > Such commands could > be added to the control package of tcllib, if it is > believed necessary to provide such commands broadly. I > don't think there's any need to change the core to give > the proposer the power he seeks. I hope you will, and I agree the programming convenience I requested can be found without changing the core. The only reason I now see for incorporating this into the core is that there are much more efficient ways of bytecoding break-with-eval than that which the byte-compiler produces from the script-level solutions. I suspect incorporation in the core is the only way to make the compiler produce these optimal codings (but maybe Tcl9 will have interfaces that allow extensions to specify optimal bytecode even for control structures such as break-with-eval?). > I do not think this request is a good idea. It proposes a > change to [break], but really this is a proposed change to a > number of commands that respond to the TCL_BREAK > completion code. I hope you are aware that a similar critique could be directed against TIP#90. There "a number of commands" (more precisely every command defined by [proc]) are already exhibiting a special behaviour, but that is not completely satisfactory and so it needs to be changed. *Note* that I'm not saying that I want to direct such critique against TIP#90, I'm merely pointing out that what difference there is between the proposals is a difference in degree rather than something specifically distinict. Indeed, there is a script level workaround to the return -code versus control::do problem that is quite similar to the script level implementation of break-with-eval. Consider proc eproc {name arglist body} { interp alias {} $name {} eproc_call "$name " proc "$name " $arglist $body } proc returneval {script} {return -code -1 $script} proc eproc_call {args} { set code [catch [list uplevel 1 $args] res] if {$code == -1} then { set code [catch [list uplevel 1 $res] res] return -code $code $res } elseif {$code == 1} then { global errorInfo errorCode return -code error -errorinfo $errorInfo -errorcode $errorCode $res } else { return -code $code $res } } With these commands, one can instead of the definition proc b {} {control::do {return -code error} while 1} which stumbles on the fact that control::do is a proc, use the definition eproc c {} {control::do {returneval {error}} while 1} which does not. Indeed, even the $errorInfo one gets after [catch c] is (with the exception of the proc name) identical to that one gets from [catch a], where [a] is defined by proc a {} {while 1 {return -code error}} Is it reasonable to request the use of [eproc] rather than [proc]? Perhaps not, but it probably isn't substantially more reasonable to request the use of [breakeval-aware foreach] and [breakeval {script}] instead of [foreach] and [break {script}]. /Lars Hellström dgp added on 2002-05-11 04:56:32: Logged In: YES user_id=80530 For compatibility reasons, I do not think such a proposal can be considered before Tcl 9. I do not think this request is a good idea. It proposes a change to [break], but really this is a proposed change to a number of commands that respond to the TCL_BREAK completion code. It's not at all clear how some of those commands would need to adapt to this change. ([subst] ? Evaluation of Tk bindings? ) [break] has a very narrow purpose of providing at the script level the ability to return a TCL_BREAK completition code. I do not think it should be further complexified. The particular applications outlined in the proposal are worthwhile and interesting, but Tcl already provides the ability to define new looping commands and new completion codes that can implement those ideas. Such commands could be added to the control package of tcllib, if it is believed necessary to provide such commands broadly. I don't think there's any need to change the core to give the proposer the power he seeks. I'm leaving the request open for further comment, but dropping the priority. dkf added on 2002-05-01 14:57:38: Logged In: YES user_id=79902 TIP #90's Don Porter's baby; this FRQ should probably be considered by him as well. |
