Recursion: 3.1 Calling A Function From Inside The Body of That Function
Recursion: 3.1 Calling A Function From Inside The Body of That Function
Recursion
and the definition of the function that multiplies its argument by 4 or that
squares it would be identical.
Another way to try to understand the definition of the function fact is to see
it as a definition by induction of the sequence un = n!: u0 = 1, un+1 = (n +
1) * un . Although this works for this function, that does not mean it will work
in general, for example for the function
When we have a recursive function definition, for example the definition of the
factorial, it is possible to transform this definition into another, non-recursive
one, by replacing the calls of the function fact in the body of the function
50 3. Recursion
fact by calls to another function fact1, identical to fact, but defined before
it
static int fact1 (final int x) {
if (x == 0) return 1;
return x * fact1(x - 1);}
we start by defining (v1 ,m1 ) = Θk (t1 ,e,m,G), (v2 ,m2 ) = Θk (t2 ,e,m1 ,G), ...,
(vn ,mn ) = Θk (tn ,e,mn−1 ,G), then e” and m” as we have done in the previous
chapter. Next, instead of considering the object Σk (p,e”,m”,G) we consider
the object Σk−1 (p,e”,m”,G). A notable exception occurs in the case where k
= 0, and in this case, the Σ0 function is not defined for this expression.
Once the family of Σk functions is defined, we define the Σ function
Σ(p,e,m,G) = limk Σk (p,e,m,G).
f = (x → f(x))
f = (x → 2 * f(x))
What does the call fact(-100) return if we define the function fact as
follows?
Why? And what does the call fact(-100) return if we define the function
fact as follows?
Why?
3.3 Caml
In Caml, we execute the body of the function in the environment in which the
function was declared, extended by the declaration of its arguments. Because of
this fact, only functions declared before the function f are accessible within the
body of f, and the declaration
let fact x = if x = 0 then 1 else x * fact(x - 1)
in print_int (fact 6)
is invalid.
To be able to use the function fact within its own definition, you must use
a new construct let rec f x1 ... xn = t in p
let rec fact x = if x = 0 then 1 else x * fact(x - 1)
in print_int (fact 6)
and, at each function call, the definition of the function fact is then added to
the environment in which the body of the function is executed.
When two functions are mutually recursive, you cannot declare them as
follows
let rec even x = if x = 0 then true else odd(x - 1)
in let rec odd x = if x = 0 then false else even(x - 1)
in print_bool(even 7)
54 3. Recursion
because the environment in which the function even is executed does not contain
the function odd, so we have to use a special construct for mutually recursive
functions let rec f x1 ... xn = t and g y1 ... yp = u and ... For ex-
ample
let rec even x = if x = 0 then true else odd (x - 1)
and odd x = if x = 0 then false else even (x - 1)
in print_bool (even 7)
3.4 C
In C, as in Caml, the body of the function is executed in the environment in
which this function was declared, extended with the declaration of its arguments.
Because of this, only functions declared before a function f are accessible in the
body of f.
However, in order to allow for recursion, at each function call, the definition
of the function f is added to the environment in which the body of the function is
executed. This is exactly what happens in Caml with let rec, so the declaration
of a function in C is more like Caml’s let rec than it is like let. Therefore,
the following program
int fact (const int x) {
if (x == 0) return 1;
return x * fact(x - 1);}
int main () {
printf("%d\n",fact(6));
return 0;}
is valid, and returns 720.
When the definitions of several functions, for example two functions f and
g, are mutually recursive, we must start by prototyping the function g to allow
g to be called within the body of f. Prototyping a function defines its argument
types and its return type, and you can then define the actual function later in
the program.
int main () {
printf("%d\n",even(7));
return 0;}
r = 1;
for (i = 1; i <= x; i = i + 1) {r = r * i;}
return r;}
and recursively
static int fact (final int x) {
if (x == 0) return 1;
return x * fact(x - 1);}
we see that the first uses assignments: r = 1;, i = 1;, i = i + 1; and r = r
* i;, while the second does not. It is therefore possible to program the factorial
function without using assignments.
More generally, we can consider a sub-language of Java in which we remove
assignment. In this case, all variables can be declared as constant and, in the
definition of the Σ function, the memory state is always empty. Sequences and
loops in this case become useless. We are left with a shell of Java composed
of variable declarations, function calls, arithmetical and logical operations and
tests. This sub-language is called the functional core of Java. We can also define
the functional core of many programming languages.
Surprisingly, this functional core is just as powerful as Java as a whole.
For each expression t of Java, we associate the partial function that maps
the integer n to the value v such that (v,m’) = Θ(t,[x = n],[],G), and
for each statement p in Java we associate the partial function that maps the
integer n to the value v such that (return,v,m) = Σ(p,[x = n],[],G). A
56 3. Recursion
The only movement allowed is to move a single disk from the top of one
column to the top of another column, with the condition that you can
never place a bigger disk on top of a smaller one. We write n -> n’ the
movement of a disk from column n to column n’. The goal of the game
is to move all of the disks from the left column to the right column.
This curve is defined as the limit of the sequence of curves where the
first curve is a segment
and where each element is obtained from the previous one, by dividing
each segment in 3, and replacing the middle segment with an equilat-
eral triangle with one side removed. The second iteration of the Koch
snowflake is thus
the third
58 3. Recursion