Posted to tcl by kbk at Thu Dec 26 21:08:04 GMT 2013view pretty
# coroutine_iterator.tcl -- # # Implements a 'foreach' loop that uses a coroutine to manage the # iteration, and cleans up properly on unusual terminations. # # Copyright (c) 2013 by Kevin B. Kenny # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # #------------------------------------------------------------------------------ package require Tcl 8.6 namespace eval coroutine { namespace eval iterator { variable gensym 0; # Sequence number for generated symbols namespace export foreach } } # coroutine::iterator::foreach -- # # Iterate over the results of a coroutine # # Usage: # coroutine::iterator::foreach var initCommand script # # Parameters: # var - Name of the variable in caller's scope that # will hold the values the procedure is iterating over. # initCommand - Command and arguments that will be the main # procedure of the coroutine. The procedure is # expected to yield each of the iteration results # in turn, and then return to indicate the end of # the loop. # script - Script to execute for each [yield]ed value, with # the [yield]ed value in $var. # # Results: # None. # # Side effects: # Launches a coroutine with the given 'initCommand' and runs # it to completion, executing the given script one on each # [yield]ed result. proc coroutine::iterator::foreach {var initCommand script} { variable gensym set coro [namespace current]::coro[incr gensym] upvar 1 $var value try { for {set value [coroutine $coro {*}$initCommand]} \ {[namespace which $coro] ne {}} \ {set value [$coro]} { try { uplevel 1 $script } on error {message options} { dict incr options -level 1 return -options $options $message } on return {retval options} { dict incr options -level 1 return -options $options $retval } on break {} { break } on continue {} { continue } } } finally { catch {rename $coro {}} } } package provide coroutine::iterator 1.0 if {![info exists ::argv0] || $::argv0 ne [info script]} { return } # Example: proc doit {n} { for {set i 0} {$i < $n} {incr i} { yield $i } return } coroutine::iterator::foreach x {doit 10} {puts $x}