Clarify the sense in which Nim supports recursive iterators in the (#15834)
manual, the tutorial, and the `tbintree` test.
This commit is contained in:
parent
7d640e0943
commit
f17555770e
3 changed files with 44 additions and 7 deletions
|
|
@ -4031,10 +4031,13 @@ Closure iterators and inline iterators have some restrictions:
|
||||||
1. For now, a closure iterator cannot be executed at compile time.
|
1. For now, a closure iterator cannot be executed at compile time.
|
||||||
2. ``return`` is allowed in a closure iterator but not in an inline iterator
|
2. ``return`` is allowed in a closure iterator but not in an inline iterator
|
||||||
(but rarely useful) and ends the iteration.
|
(but rarely useful) and ends the iteration.
|
||||||
3. Neither inline nor closure iterators can be recursive.
|
3. Neither inline nor closure iterators can be (directly)* recursive.
|
||||||
4. Neither inline nor closure iterators have the special ``result`` variable.
|
4. Neither inline nor closure iterators have the special ``result`` variable.
|
||||||
5. Closure iterators are not supported by the js backend.
|
5. Closure iterators are not supported by the js backend.
|
||||||
|
|
||||||
|
(*) Closure iterators can be co-recursive with a factory proc which results
|
||||||
|
in similar syntax to a recursive iterator. More details follow.
|
||||||
|
|
||||||
Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
|
Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
|
||||||
default to being inline, but this may change in future versions of the
|
default to being inline, but this may change in future versions of the
|
||||||
implementation.
|
implementation.
|
||||||
|
|
@ -4129,7 +4132,41 @@ parameters of an outer factory proc:
|
||||||
for f in foo():
|
for f in foo():
|
||||||
echo f
|
echo f
|
||||||
|
|
||||||
|
The call can be made more like an inline iterator with a for loop macro:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
import macros
|
||||||
|
macro toItr(x: ForLoopStmt): untyped =
|
||||||
|
let expr = x[0]
|
||||||
|
let call = x[1][1] # Get foo out of toItr(foo)
|
||||||
|
let body = x[2]
|
||||||
|
result = quote do:
|
||||||
|
block:
|
||||||
|
let itr = `call`
|
||||||
|
for `expr` in itr():
|
||||||
|
`body`
|
||||||
|
|
||||||
|
for f in toItr(mycount(1, 4)): # using early `proc mycount`
|
||||||
|
echo f
|
||||||
|
|
||||||
|
Because of full backend function call aparatus involvment, closure iterator
|
||||||
|
invocation is typically higher cost than inline iterators. Adornment by
|
||||||
|
a macro wrapper at the call site like this is a possibly useful reminder.
|
||||||
|
|
||||||
|
The factory ``proc``, as an ordinary procedure, can be recursive. The
|
||||||
|
above macro allows such recursion to look much like a recursive iterator
|
||||||
|
would. For example:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
proc recCountDown(n: int): iterator(): int =
|
||||||
|
result = iterator(): int =
|
||||||
|
if n > 0:
|
||||||
|
yield n
|
||||||
|
for e in toItr(recCountDown(n - 1)):
|
||||||
|
yield e
|
||||||
|
|
||||||
|
for i in toItr(recCountDown(6)): # Emits: 6 5 4 3 2 1
|
||||||
|
echo i
|
||||||
|
|
||||||
Converters
|
Converters
|
||||||
==========
|
==========
|
||||||
|
|
@ -4619,8 +4656,8 @@ The following example shows a generic binary tree can be modeled:
|
||||||
|
|
||||||
iterator preorder*[T](root: BinaryTree[T]): T =
|
iterator preorder*[T](root: BinaryTree[T]): T =
|
||||||
# Preorder traversal of a binary tree.
|
# Preorder traversal of a binary tree.
|
||||||
# Since recursive iterators are not yet implemented,
|
# This uses an explicit stack (which is more efficient than
|
||||||
# this uses an explicit stack (which is more efficient anyway):
|
# a recursive iterator factory).
|
||||||
var stack: seq[BinaryTree[T]] = @[root]
|
var stack: seq[BinaryTree[T]] = @[root]
|
||||||
while stack.len > 0:
|
while stack.len > 0:
|
||||||
var n = stack.pop()
|
var n = stack.pop()
|
||||||
|
|
|
||||||
|
|
@ -511,8 +511,8 @@ containers:
|
||||||
|
|
||||||
iterator preorder*[T](root: BinaryTree[T]): T =
|
iterator preorder*[T](root: BinaryTree[T]): T =
|
||||||
# Preorder traversal of a binary tree.
|
# Preorder traversal of a binary tree.
|
||||||
# Since recursive iterators are not yet implemented,
|
# This uses an explicit stack (which is more efficient than
|
||||||
# this uses an explicit stack (which is more efficient anyway):
|
# a recursive iterator factory).
|
||||||
var stack: seq[BinaryTree[T]] = @[root]
|
var stack: seq[BinaryTree[T]] = @[root]
|
||||||
while stack.len > 0:
|
while stack.len > 0:
|
||||||
var n = stack.pop()
|
var n = stack.pop()
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,8 @@ proc find*[Ty2](b: PBinaryTree[Ty2], data: Ty2): bool =
|
||||||
|
|
||||||
iterator preorder*[T](root: PBinaryTree[T]): T =
|
iterator preorder*[T](root: PBinaryTree[T]): T =
|
||||||
# Preorder traversal of a binary tree.
|
# Preorder traversal of a binary tree.
|
||||||
# Since recursive iterators are not yet implemented,
|
# This uses an explicit stack (which is more efficient than
|
||||||
# this uses an explicit stack:
|
# a recursive iterator factory).
|
||||||
var stack: seq[PBinaryTree[T]] = @[root]
|
var stack: seq[PBinaryTree[T]] = @[root]
|
||||||
while stack.len > 0:
|
while stack.len > 0:
|
||||||
var n = stack.pop()
|
var n = stack.pop()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue