Open main menu
Home
Random
Recent changes
Special pages
Community portal
Preferences
About Wikipedia
Disclaimers
Incubator escapee wiki
Search
User menu
Talk
Dark mode
Contributions
Create account
Log in
Editing
Mutual recursion
(section)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
==Examples== ===Datatypes=== {{further|Recursive data type}} The most important basic example of a datatype that can be defined by mutual recursion is a [[tree (data structure)|tree]], which can be defined mutually recursively in terms of a forest (a list of trees). Symbolically: f: <nowiki>[t[1], ..., t[k]]</nowiki> t: v f A forest ''f'' consists of a list of trees, while a tree ''t'' consists of a pair of a value ''v'' and a forest ''f'' (its children). This definition is elegant and easy to work with abstractly (such as when proving theorems about properties of trees), as it expresses a tree in simple terms: a list of one type, and a pair of two types. Further, it matches many algorithms on trees, which consist of doing one thing with the value, and another thing with the children. This mutually recursive definition can be converted to a singly recursive definition by [[Inline expansion|inlining]] the definition of a forest: t: v <nowiki>[t[1], ..., t[k]]</nowiki> A tree ''t'' consists of a pair of a value ''v'' and a list of trees (its children). This definition is more compact, but somewhat messier: a tree consists of a pair of one type and a list of another, which require disentangling to prove results about. In [[Standard ML]], the tree and forest datatypes can be mutually recursively defined as follows, allowing empty trees:{{sfn|Harper|2000|loc="[https://www.cs.cmu.edu/~rwh/introsml/core/datatypes.htm Date Types]"}} <syntaxhighlight lang="sml"> datatype 'a tree = Empty | Node of 'a * 'a forest and 'a forest = Nil | Cons of 'a tree * 'a forest </syntaxhighlight> ===Computer functions=== Just as algorithms on recursive datatypes can naturally be given by recursive functions, algorithms on mutually recursive data structures can be naturally given by mutually recursive functions. Common examples include algorithms on trees, and [[recursive descent parser]]s. As with direct recursion, [[tail call optimization]] is necessary if the recursion depth is large or unbounded, such as using mutual recursion for multitasking. Note that tail call optimization in general (when the function called is not the same as the original function, as in tail-recursive calls) may be more difficult to implement than the special case of tail-recursive call optimization, and thus efficient implementation of mutual tail recursion may be absent from languages that only optimize tail-recursive calls. In languages such as [[Pascal (programming language)|Pascal]] that require declaration before use, mutually recursive functions require [[forward declaration]], as a forward reference cannot be avoided when defining them. As with directly recursive functions, a [[Recursion (computer science)#Wrapper function|wrapper function]] may be useful, with the mutually recursive functions defined as [[nested function]]s within its scope if this is supported. This is particularly useful for sharing state across a set of functions without having to pass parameters between them. ====Basic examples==== A standard example of mutual recursion, which is admittedly artificial, determines whether a non-negative number is even or odd by defining two separate functions that call each other, decrementing by 1 each time.{{sfn|Hutton|2007|loc=6.5 Mutual recursion, pp. [https://books.google.com/books?id=olp7lAtpRX0C&pg=PA53&dq=%22mutual+recursion%22 53–55]}} In C: <syntaxhighlight lang=C> bool is_even(unsigned int n) { if (n == 0) return true; else return is_odd(n - 1); } bool is_odd(unsigned int n) { if (n == 0) return false; else return is_even(n - 1); } </syntaxhighlight> These functions are based on the observation that the question ''is 4 even?'' is equivalent to ''is 3 odd?'', which is in turn equivalent to ''is 2 even?'', and so on down to 0. This example is mutual [[single recursion]], and could easily be replaced by iteration. In this example, the mutually recursive calls are [[tail call]]s, and tail call optimization would be necessary to execute in constant stack space. In C, this would take ''O''(''n'') stack space, unless rewritten to use jumps instead of calls.<ref>"[http://www.cs.bu.edu/~hwxi/ATS/DOCUMENT/TUTORIALATS/HTML/c244.html Mutual Tail-Recursion]" and "[http://www.cs.bu.edu/~hwxi/ATS/TUTORIAL/contents/tail-recursive-functions.html Tail-Recursive Functions]", ''[http://www.cs.bu.edu/~hwxi/ATS/DOCUMENT/TUTORIALATS/HTML/book1.html A Tutorial on Programming Features in ATS],'' Hongwei Xi, 2010</ref> This could be reduced to a single recursive function <code>is_even</code>. In that case, <code>is_odd</code>, which could be inlined, would call <code>is_even</code>, but <code>is_even</code> would only call itself. As a more general class of examples, an algorithm on a tree can be decomposed into its behavior on a value and its behavior on children, and can be split up into two mutually recursive functions, one specifying the behavior on a tree, calling the forest function for the forest of children, and one specifying the behavior on a forest, calling the tree function for the tree in the forest. In Python: <syntaxhighlight lang="python"> def f_tree(tree) -> None: f_value(tree.value) f_forest(tree.children) def f_forest(forest) -> None: for tree in forest: f_tree(tree) </syntaxhighlight> In this case the tree function calls the forest function by single recursion, but the forest function calls the tree function by [[multiple recursion]]. Using the Standard ML datatype above, the size of a tree (number of nodes) can be computed via the following mutually recursive functions:{{sfn|Harper|2000|loc="[https://www.cs.cmu.edu/~rwh/introsml/core/datatypes.htm Datatypes]"}} <syntaxhighlight lang="sml"> fun size_tree Empty = 0 | size_tree (Node (_, f)) = 1 + size_forest f and size_forest Nil = 0 | size_forest (Cons (t, f')) = size_tree t + size_forest f' </syntaxhighlight> A more detailed example in [[Scheme (programming language)|Scheme]], counting the leaves of a tree:{{sfn|Harvey|Wright|1999|loc=V. Abstraction: 18. Trees: Mutual Recursion, pp. [https://books.google.com/books?id=igJRhp0KGn8C&pg=PA310&dq=%22mutual%20recursion%22 310–313]}} <syntaxhighlight lang=scheme> (define (count-leaves tree) (if (leaf? tree) 1 (count-leaves-in-forest (children tree)))) (define (count-leaves-in-forest forest) (if (null? forest) 0 (+ (count-leaves (car forest)) (count-leaves-in-forest (cdr forest))))) </syntaxhighlight> These examples reduce easily to a single recursive function by inlining the forest function in the tree function, which is commonly done in practice: directly recursive functions that operate on trees sequentially process the value of the node and recurse on the children within one function, rather than dividing these into two separate functions. ====Advanced examples==== A more complicated example is given by [[recursive descent parser]]s, which can be naturally implemented by having one function for each [[Production (computer science)|production rule]] of a grammar, which then mutually recurse; this will in general be multiple recursion, as production rules generally combine multiple parts. This can also be done without mutual recursion, for example by still having separate functions for each production rule, but having them called by a single controller function, or by putting all the grammar in a single function. Mutual recursion can also implement a [[finite-state machine]], with one function for each state, and single recursion in changing state; this requires tail call optimization if the number of state changes is large or unbounded. This can be used as a simple form of [[cooperative multitasking]]. A similar approach to multitasking is to instead use [[coroutine]]s which call each other, where rather than terminating by calling another routine, one coroutine yields to another but does not terminate, and then resumes execution when it is yielded back to. This allows individual coroutines to hold state, without it needing to be passed by parameters or stored in shared variables. There are also some algorithms which naturally have two phases, such as [[minimax]] (min and max), which can be implemented by having each phase in a separate function with mutual recursion, though they can also be combined into a single function with direct recursion. ===Mathematical functions=== In mathematics, the [[Hofstadter Female and Male sequences]] are an example of a pair of integer sequences defined in a mutually recursive manner. Fractals can be computed (up to a given resolution) by recursive functions. This can sometimes be done more elegantly via mutually recursive functions; the [[Sierpiński curve]] is a good example.
Edit summary
(Briefly describe your changes)
By publishing changes, you agree to the
Terms of Use
, and you irrevocably agree to release your contribution under the
CC BY-SA 4.0 License
and the
GFDL
. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.
Cancel
Editing help
(opens in new window)