summary refs log tree commit diff stats
path: root/doc/manual.rst
diff options
context:
space:
mode:
authorc-blake <c-blake@users.noreply.github.com>2020-11-04 10:56:22 -0500
committerGitHub <noreply@github.com>2020-11-04 16:56:22 +0100
commitf17555770e816580b475674be23458da51b10dfd (patch)
tree5e6dbb605c693811762224e8866363ed3c15bcf0 /doc/manual.rst
parent7d640e0943679a772f9595ef3be56fe2158b0912 (diff)
downloadNim-f17555770e816580b475674be23458da51b10dfd.tar.gz
Clarify the sense in which Nim supports recursive iterators in the (#15834)
manual, the tutorial, and the `tbintree` test.
Diffstat (limited to 'doc/manual.rst')
-rw-r--r--doc/manual.rst43
1 files changed, 40 insertions, 3 deletions
diff --git a/doc/manual.rst b/doc/manual.rst
index d09bb4c76..d9f9cc9b9 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -4031,10 +4031,13 @@ Closure iterators and inline iterators have some restrictions:
 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
    (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.
 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
 default to being inline, but this may change in future versions of the
 implementation.
@@ -4129,7 +4132,41 @@ parameters of an outer factory proc:
   for f in foo():
     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
 ==========
@@ -4619,8 +4656,8 @@ The following example shows a generic binary tree can be modeled:
 
   iterator preorder*[T](root: BinaryTree[T]): T =
     # Preorder traversal of a binary tree.
-    # Since recursive iterators are not yet implemented,
-    # this uses an explicit stack (which is more efficient anyway):
+    # This uses an explicit stack (which is more efficient than
+    # a recursive iterator factory).
     var stack: seq[BinaryTree[T]] = @[root]
     while stack.len > 0:
       var n = stack.pop()