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}