корутина
Имя
корутина, yield, yieldto — Создание и выдача значений из корутинСинопсис
корутина имя команда ?арг...?yield ?значение?
yieldto команда ?арг...?
имя ?значение...?
Описание
Команда корутина создаёт новый контекст корутины (с ассоциированной командой) с именем имя и выполняет этот контекст, вызывая команду, передавая остальные аргументы без дальнейшей интерпретации. После того, как команда вернётся нормально или с исключением (например, ошибкой), контекст корутины имя удаляется.Внутри контекста значения могут генерироваться в качестве результатов с помощью команды yield; если значение не указано, используется пустая строка. При вызове этого, контекст приостановит выполнение, а команда корутина вернёт аргумент yield. Выполнение контекста можно затем возобновить, вызвав команду контекста, необязательно передав единственное значение, которое будет использоваться в качестве результата вызова yield, который привел к приостановке контекста. Если контекст корутины никогда не использует yield и вместо этого возвращается обычным образом, результат команды корутина будет результатом вычисления контекста.
Корутина также может приостановить своё выполнение с помощью команды yieldto, которая вместо возврата уступает выполнение некоторой команде, называемой команда (разрешаемая в контексте корутины), и которой могут быть переданы любое количество аргументов. Поскольку каждая корутина имеет команду контекста, yieldto может использоваться для передачи управления непосредственно от одной корутины к другой (это рекомендуется только если две корутины ожидают этого), но любая команда может быть целью. Если корутина приостановлена этим механизмом, обработка корутины может быть возобновлена путём вызова команды контекста, необязательно передавая любое количество аргументов. Значение возврата вызова yieldto будет списком аргументов, переданных команде контекста; вызывающая сторона решает, что делать с этими значениями.
Рекомендуемый способ написания версии yield, который позволяет возобновление с множеством аргументов, заключается в использовании yieldto и команды return, как в этом примере:
proc yieldm {value} {
yieldto return -level 0 $value
} Корутину также можно удалить, уничтожив команду имя, а имя текущей корутины можно получить, используя info корутина. Если в реализации корутины есть следы удаления переменных, они сработают в тот момент, когда корутина будет явно удалена (или, естественно, если команда вернётся обычным образом).
В момент вызова команды текущее пространство имён будет глобальным пространством имён, и над ним не будет стековых кадров (в смысле upvar и uplevel). Однако, какая команда будет вызвана, определяется в пространстве имён, из которого была вызвана команда корутина.
Примеры
Этот пример показывает корутину, которая будет генерировать бесконечную последовательность чётных значений, и цикл, который потребляет первые десять из них.proc allNumbers {} {
yield
set i 0
while 1 {
yield $i
incr i 2
}
}
coroutine nextNumber allNumbers
for {set i 0} {$i < 10} {incr i} {
puts "received [nextNumber]"
}
rename nextNumber {} В этом примере корутина выполняет суммирование переданных ей аргументов.
coroutine accumulator apply {{} {
set x 0
while 1 {
incr x [yield $x]
}
}}
for {set i 0} {$i < 10} {incr i} {
puts "$i -> [accumulator $i]"
} Этот пример демонстрирует использование корутин для реализации классического алгоритма Решета Эратосфена для нахождения простых чисел. Обратите внимание на создание корутин внутри корутины.
proc filterByFactor {source n} {
yield [info coroutine]
while 1 {
set x [$source]
if {$x % $n} {
yield $x
}
}
}
coroutine allNumbers apply {{} {while 1 {yield [incr x]}}}
coroutine eratosthenes apply {c {
yield
while 1 {
set n [$c]
yield $n
set c [coroutine prime$n filterByFactor $c $n]
}
}} allNumbers
for {set i 1} {$i <= 20} {incr i} {
puts "prime#$i = [eratosthenes]"
} Этот пример показывает, как значение может передаваться по группе из трёх корутин, которые передают друг другу управление:
proc juggler {name target {value ""}} {
if {$value eq ""} {
set value [yield [info coroutine]]
}
while {$value ne ""} {
puts "$name : $value"
set value [string range $value 0 end-1]
lassign [yieldto $target $value] value
}
}
coroutine j1 juggler Larry [
coroutine j2 juggler Curly [
coroutine j3 juggler Moe j1]] "Nyuck!Nyuck!Nyuck!" Подробная семантика
Этот пример демонстрирует, что корутины начинаются с глобального пространства имён, и что разрешение команды происходит до создания стека корутины.proc report {where level} {
# Where was the caller called from?
set ns [uplevel 2 {namespace current}]
yield "made $where $level context=$ns name=[info coroutine]"
}
proc example {} {
report outer [info level]
}
namespace eval demo {
proc example {} {
report inner [info level]
}
proc makeExample {} {
puts "making from [info level]"
puts [coroutine coroEg example]
}
makeExample
} Что даёт вывод ниже. В частности, мы можем видеть, что произошла манипуляция стеком (сравнивая уровни с первой и второй строки) и что родительский уровень в корутине - это глобальное пространство имён. Мы также можем видеть, что имена корутин локальны для текущего пространства имён, если не квалифицированы, и что корутины могут приостанавливаться на глубине (например, в вызываемых процедурах).
making from 2 made inner 1 context=:: name=::demo::coroEg
См. также
apply, info, proc, return
Licensed under Tcl/Tk terms
https://www.tcl.tk/man/tcl/TclCmd/coroutine.htm
Licensed under Tcl/Tk terms
https://www.tcl.tk/man/tcl/TclCmd/coroutine.htm