From: Tom J. <tom...@gm...> - 2010-08-28 06:00:15
|
On Fri, Aug 27, 2010 at 12:22 PM, Robert Seeger <rhs...@gm...> wrote: > So, what I have in my mind as uses for coroutines are the following: > > As generators (streams) > proc integers {min max step} { > while {$min < $max} { > yield $min > incr min $step > } > } > # coforeach would be a version of foreach that gets it's values > # by calling the supplied command each iteration of the loop > # The caller of the coroutine needs to know what value it yielded, > # but doesn't need to pass a value back into it > coforeach int [integers 1 10 1] { > puts $int > } > > Producer/Consumer pattern: > # The consumers need to do some setup which sets up the > # variables (open file here) they need to do their work > # The consumers need values passed in each iteration (level, message) > proc file_logger {filename} { > set fd [open $filename w] > while {true} { > lassign [yield] level message > puts $fd "[string toupper $level]: $message" > } > } > > set error_log [file_logger error.log] > set standard_log [file_logger output.log] > > set fd [open "logtoparse.log" r] > while {[gets $fd line] >= 0} { > lassign [parse_log_line $line] level message > switch -exact -- $level { > error { > $error_log $level $message > } > default { > $standard_log $level $message > } > } > } > > There is a third pattern, which yieldTo supports, where coroutines call back > and forth to each other. I don't have sample code for that because I haven't > run into a use case for it. If you google, however, I'm sure you'll find > places where people do have uses for it. Here is a working example of passing control among several coroutines: http://rmadilo.com/files/coroutines/coros/symmetric/symmetric-dice2.tcl source symmetric.tcl set controller [::sequencing::init DiceGame] foreach id {A B C D E F} { ::sequencing::addCoroutine $controller Player$id Player $id } ::sequencing::addCoroutine $controller Judge DiceGameJudge set namespace [::sequencing::prepareSequence $controller] namespace eval $namespace { variable Sum variable Players proc Player { id } { variable Sum variable Players set sum 0 set Sum(Player$id) $sum lappend Players Player$id while {1} { Resume Judge set sum [expr {$sum + ( int((ceil(rand()*10000)) )%6)+1}] set Sum(Player$id) $sum } } proc getCurrentPlayer { roll } { variable Players variable TotalPlayers lindex $Players [expr {($roll % $TotalPlayers)}] } proc DiceGameJudge { } { variable Players variable Sum variable TotalPlayers [llength $Players] set roll 1 Resume Judge set CurrentPlayer [getCurrentPlayer $roll] while {1} { Resume $CurrentPlayer if {$Sum($CurrentPlayer) >= 100} { break } incr roll set CurrentPlayer [getCurrentPlayer $roll] } puts "The winner is player $CurrentPlayer" puts "Number of dice throws = $roll\n" foreach player $Players { puts "Player $player score = $Sum($player)" } Resume NONE } coroutine Game $::sequencing::ControllerProc($controller) Game } The dice game has any number of players and a judge. Each player takes a turn rolling a dice and the result is added to their previous total. Each player is a coroutine, the judge is a different coroutine. Once a player rolls the dice, the judge is resumed. The judge determines if the player's score has exceeded 99 declares a winner and prints every players score, this terminates the game. Otherwise, the judge resumes the next player. The game itself is also a coroutine. tom jackson |