======================
Functional recursion
======================
“Recursion” is a programming technique whereby the algorithm to solve
a problem is decomposed into a *base case* where the result is simple,
and a *general case* where the result is a computation that reuses the
computation for a simpler case.
The technique is closely related to the *principle of induction* in
mathematics. Actually, the little-known but very effective trick to
learn to use recursion is to first ignore all aspects related to
programming and concentrate on the mathematical notion of induction as
a first step.
.. contents::
Inductive definitions in maths
==============================
An *inductive definition* in maths is the definition of some object or
concept :math:`A(n)`, depending on a non-negative integer parameter
:math:`n`, according to the following scheme: a) the value of a finite
number of base cases, at least one (eg. :math:`A(0)`) is given; and b)
a rule is given for obtaining the value of :math:`A(n)` from :math:`n`
and fixed number of values in :math:`A(0)...A(n-1)`.
Some typical inductive definitions:
============= ===================== ================================
Function Base case(s) General case
============= ===================== ================================
:math:`n!` :math:`0! = 1` :math:`n! = n \times (n-1)!`
:math:`x^n` :math:`x^0 = 1` :math:`x^n = x \times x^{n-1}`
Fibonacci :math:`F(0)=F(1)=1` :math:`F(n) = F(n-1) + F(n-2)`
============= ===================== ================================
For the purpose of this course, you need to learn how to write all
inductive definitions in maths according to the same fixed format:
- on the first line, the function name, the domain of its parameter(s) and its image set;
- on the second and following lines, the base cases, expressed using the function name;
- on the last line, the general case, expressed using the function name.
For example:
:math:`\left\{\begin{array}{rcl} \text{fact}: \mathbb{N} & \mapsto & \mathbb{N}^* \\ \text{fact}(0) & = & 1 \\ \text{fact}(n) & = & n \times \text{fact}(n-1) \\ \end{array}\right.`
(Now how we use “fact(n)” instead of “n!” here.)
:math:`\left\{\begin{array}{rcl} \text{pow}: \mathbb{R}\times\mathbb{N} & \mapsto & \mathbb{R} \\ \text{pow}(x, 0) & = & 1 \\ \text{pow}(x, n) & = & x \times \text{pow}(x, n-1) \\ \end{array}\right.`
(Note how we use “pow(x,n)” instead of “:math:`x^n`” here.)
:math:`\left\{\begin{array}{rcl} \text{fib}: \mathbb{N} & \mapsto & \mathbb{N}^* \\ \text{fib}(0) & = & 1 \\ \text{fib}(1) & = & 1 \\ \text{fib}(n) & = & \text{fib}(n-1) + \text{fib}(n-2) \\ \end{array}\right.`
Finding your own inductive definition
=====================================
The idea to use inductive definition should come to mind automatically
as soon as you are faced with a description of what a function does based
on some examples using an ellipsis “...”.
Consider for example the following description:
“Implement a function ``sine(x,n)`` which approximates the trigonometric
function of the same name using its Taylor series with n terms, that is:
``sine(x,n) = x - x^3/3! + x^5/5! - x^7/7! + ...``
with n terms, with n at least 1.”
This description uses the ellipsis “...” so it invites an inductive definition.
Once you have this idea, you can achive an inductive definition as follows:
1. recognize in the description example which are the results for the
*most simple form(s)* of the function, and write this down. This
becomes the base case.
2. *reformulate the function at a step n using the result at step n-1,
n and/or the other parameters.* Then write this down. This becomes
the general case.
3. Write down the resulting definition using the canonical format
described above.
In the example above, the most general form is when n = 1:
:math:`\text{sine}(x, 1) = x`
Then at a given term n:
:math:`\text{sine}(x, n) = \text{sine}(x, n-1) + (-1)^n\frac{x^{(2n-1)}}{(2n-1)!}`
So we can then write using the canonical format:
:math:`\left\{\begin{array}{rcl} \text{sine}: \mathbb{R}\times\mathbb{N} & \mapsto & \mathbb{R} \\ \text{sine}(x, 1) & = & x \\ \text{sine}(x, n) & = & \text{sine}(x, n-1) + (-1)^n\frac{x^{(2n-1)}}{(2n-1)!} \\ \end{array}\right.`
Likewise, consider the following description:
“Implement a function :math:`p(n)` which for any :math:`n\geq 1` is equal to the following::
p(1) = 1
p(2) = 1 * 2 * 1
p(3) = 1 * 2 * 1 * 3 * 1 * 2 * 1
p(4) = 1 * 2 * 1 * 3 * 1 * 2 * 1 * 4 * 1 * 2 * 1 * 3 * 1 * 2 * 1
...
As a first step we need to recognize the most simple case, this is given for p(1).
Then we need to recognize how p(n) is expressed from p(n-1). For this
we can look how p(2) is made from p(1)::
p(2) = p(1) * 2 * p(1)
Then how p(3) is made from p(2)::
p(3) = p(2) * 3 * p(2)
by trying out the first few terms, we recognize the pattern and we can generalize::
p(n) = p(n-1) * n * p(n-1)
From this we can express the full mathematical definition:
:math:`\left\{ \begin{array}{rcl} p: \mathbb{N}^* & \mapsto & \mathbb{N}^* \\ p(1) &=& 1 \\ p(n) &= & p(n-1) \times n \times p(n-1) \\ \end{array}\right.`
From mathematical definition to recursive function
==================================================
Once you have obtained a mathematical inductive definition, it is
possible to transcribe the definition directly into a programming
language, to obtain a *recursive function*. To do this, proceed as
follows:
1. use the name of the function in the first line of the mathematical definition.
2. define the argument and return types using the domain and image sets of the mathematical definition.
3. inside the function body, use a ``if`` construct to detect the base
cases. For each base case, use ``return`` to set the corresponding
value.
4. in the ``else`` branch that does not correspond to the base case, write
the general case. Do not hesitate to transcribe the mathematical definition without changes. It
is OK that the function name appears in your transcription.
For example, consider the mathematical definition earlier:
:math:`\left\{\begin{array}{rcl} \text{fact}: \mathbb{N} & \mapsto & \mathbb{N}^* \\ \text{fact}(0) & = & 1 \\ \text{fact}(n) & = & n \times \text{fact}(n-1) \\ \end{array}\right.`
We take the name of the function, its domain and image and we transcribe:
.. code:: java
int fact(int n) {
Then we use a ``if`` construct for the base case:
.. code:: java
if (n == 0)
return 1;
Then we finish with the general case:
.. code:: java
else
return n * fact(n-1);
}
This gives us the following recursive function:
.. code:: java
int fact(int n)
{
if (n == 0)
return 1;
else
return n * fact(n-1);
}
Likewise for the following definition:
:math:`\left\{ \begin{array}{rcl} p: \mathbb{N}^* & \mapsto & \mathbb{N}^* \\ p(1) &=& 1 \\ p(n) &= & p(n-1) \times n \times p(n-1) \\ \end{array}\right.`
The resulting code is:
.. code:: java
int p(int n)
{
if (n == 1)
return 1;
else
return p(n-1) * n * p(n-1);
}
Likewise, for the following:
:math:`\left\{\begin{array}{rcl} \text{sine}: \mathbb{R}\times\mathbb{N} & \mapsto & \mathbb{R} \\ \text{sine}(x, 1) & = & x \\ \text{sine}(x, n) & = & \text{sine}(x, n-1) + (-1)^n\frac{x^{(2n-1)}}{(2n-1)!} \\ \end{array}\right.`
The resulting code is:
.. code:: java
double sine(double x, int n)
{
if (n == 1)
return x;
else
return sine(x, n-1) + sign(n) * pow(x, 2*n-1) / fact(2*n-1);
}
// Here we need some helper functions because
// Java does not have a native operator for
// exponentiation:
int sign(n)
{
if (n % 2 == 0)
return 1;
else
return -1;
}
// Notice how pow() is itself a recursive function.
double pow(double x, int n)
{
if (n == 0)
return 1;
else
return x * pow(n-1);
}
Important concepts
==================
- *inductive definition* in maths;
- standard format (presentation) for inductive definitions;
- *base case* and *general case*;
- process to recognize an inductive definition;
- process to translate an inductive definition in math into a recursive function in Java;
Further reading
===============
- Think Java, sections 4.8 & 4.9 (pp. 44-46)
- Introduction to Programming, section 9.1 (pp. 423-435)
- Absolute Java, chapter 11 (pp. 648-687)
----
Copyright and licensing
=======================
Copyright © 2014, Raphael ‘kena’ Poss.
Permission is granted to distribute, reuse and modify this document
according to the terms of the Creative Commons Attribution-ShareAlike
4.0 International License. To view a copy of this license, visit
`http://creativecommons.org/licenses/by-sa/4.0/
`_.