Vous êtes sur la page 1sur 15

Recursion

Prof. Geraint A. Wiggins


Centre for Cognition, Computation and Culture
Goldsmiths College, University of London

Contents
• Recursion in general
• Recursion in Prolog
• Recursion on explicit structures
• Recursion on implicit structures
• Building recursive structures
Recursion in general

• Recursion is the idea of defining something in


terms of itself

• It is very closely bound up with the idea of


mathematical induction

• It allows us to make very clear statements of


algorithms. . .

• . . . because it is a very natural way to think

• Optimisations mean that programming recursively


can be efficient, too

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 2
Some Recursive Definitions

• In Prolog, a list is either an empty list or a term


connected by ‘.’ to another list

• Someone’s ancestor can be one of their parents or


an ancestor of one of their parents

• We can sort a list of numbers into order by picking


out the smallest, and then sorting the rest

• We can sort a list of numbers into order by picking


one, say X, dividing the rest into two groups,
bigger and smaller than X, and sorting the two
groups

• We can reverse a list of terms by taking the first


element off, reversing the rest, and putting the first
element on the end
“Recursion”, CIS335: Logic Programming,
Goldsmiths’ College, London 3
Some Recursive Predicates

• Test if a term is a list


list( [] ).
list( [ |Tail] ) :- list( Tail ).

• Find an ancestor
ancestor( Old, Young ) :-
parent( Old, Young ).
ancestor( Old, Young ) :-
parent( Old, Middle ),
ancestor( Middle, Young ).

• Sort a list of numbers


sort( [], [] ).
sort( List, [Smallest|Sorted] ) :-
\+ List = [],
smallest( List, Smallest, Rest ),
sort( Rest, Sorted ).

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 4
Some Recursive Predicates

• Sort a list of numbers


sort( [], [] ).
sort( [First|Rest], Ans ) :-
split( First, Rest, Small, Big ),
sort( Small, Front ),
sort( Big, Back ),
append( Front, [First|Back], Ans ).

• Reverse a list
reverse( [], [] ).
reverse( [Head|Tail], Answer ) :-
reverse( Tail, RevTail ),
append( RevTail, [Head], Answer ).

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 5
Recursion in Prolog

• When we want to write recursive programs, we


need to think about two things:
1. How will the program terminate?
2. How will the program break up the data it
works on?

• Recursion is an example of a divide-and-conquer


strategy

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 6
Recursion in Prolog (2)

• To ensure that a program terminates, we must have


at least one base case – a non-recursive clause

• We must also ensure that something gets (in some


sense) ”reduced” each time a recursive step
happens, so that we can say when we have got to
the end

• Example – testing if a term is a list:


– The base case is when we have an empty list –
the smallest list possible
– The recursive case breaks down a non-empty
list into a head and a tail. . .
– . . . and then tests the tail, so the thing being
tested gets smaller each time.

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 7
Recursion on explicit structures

• Testing if a term is a list


list( [] ).
list( [ |Tail] ) :- list( Tail ).

?- list( [a,b,c] ).

Call: list( [a,b,c] ).


Unify: [ |Tail] = [a,b,c]

Call: list( [b,c] ).


Unify: [ |Tail’] = [b,c]

Call: list( [c] ).


Unify: [ |Tail’’] = [c]

Call: list( [] ).

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 8
Recursion on explicit structures (2)

• Example: a binary tree datatype:


– Leaves are of form leaf( value )
– Branches are of form
branch( Left, Right )

• Testing if a term is a binary tree


tree( leaf( X )).
tree( branch( X, Y )) :-
tree( X ),
tree( Y ).

• Note that we normally put the base case first, so


that Prolog tests it first!

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 9
Recursion on implicit structures

• An implicit datatype is one where there is no


visible term structure to control recursion

• Instead, the step along the datatype is defined by a


predicate, eg parent/2

• Example: parent/2
parent( john, mary ).
parent( anne, mary ).
parent( mary, dave ).
parent( tim, dave ).

• This forms an implicit binary tree, and we can use


it to control recursion just like an explicit one

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 10
Recursion on implicit structures (2)

• Example: ancestor/2
parent( john, mary ).
parent( anne, mary ).
parent( mary, dave ).
parent( tim, dave ).

ancestor( Old, Young ) :-


parent( Old, Young ).
ancestor( Old, Young ) :-
parent( Old, Middle ),
ancestor( Middle, Young ).

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 11
Recursion on implicit structures (3)

• Example run:
?- ancestor( john, dave ).

Call: ancestor( john, dave ).


Call: parent( john, dave ).
Fail.

Retry: ancestor( john, dave ).


Call: parent( john, Middle ).
Unify: Middle = mary.
Succeed: parent( john, mary ).

Call: ancestor( mary, dave ).


Call: parent( mary, dave ).
Succeed: parent( mary, dave ).
Succeed: ancestor( mary, dave ).
Succeed: ancestor( john, dave ).

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 12
Building recursive structures

• In Prolog, taking terms apart and building them


are nearly always the same thing.

• For example, in append/3:


append( [], List, List ).
append( [H|T], List, [H|BigList] ) :-
append( T, List, BigList ).

• Arguments 1 and 3 are both “broken down” or


“built up” by unification; we don’t usually use
both as inputs

• Exercise 2 shows how append/3 can be used


both to split up a list and to join lists together

• This is one reason why it is better not to think of


predicates as having “outputs” at all
“Recursion”, CIS335: Logic Programming,
Goldsmiths’ College, London 13
Building recursive structures (2)

• In the examples so far, the answer has been built


up in reverse of the breakdown of the input (no
matter which way round they are)

• Alternatively, we can use an accumulator


argument to build up an answer as we break down
the input

• Example, reverse/3:
reverse( [], Answer, Answer ).
reverse( [H|T], SoFar, Answer ) :-
reverse( T, [H|SoFar], Answer ).

?- reverse( List, [], Answer ).

(This is an example of tail-recursive optimisation.)

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 14
Building recursive structures (3)

• Example run of reverse/3:


?- reverse( [a,b,c], [], Ans ).

Call: reverse( [a,b,c], [], Ans ).


Unify: [a,b,c] = [H|T], SoFar = []

Call: reverse( [b,c], [a], Ans ).


Unify: [b,c] = [H’|T’], SoFar’ = [a]

Call: reverse( [c], [b,a], Ans ).


Unify: [c] = [H’’|T’’], SoFar’’ = [b,a]

Call: reverse( [], [c,b,a], Ans ).


Unify: Ans = Answer, Answer = [c,b,a]

“Recursion”, CIS335: Logic Programming,


Goldsmiths’ College, London 15

Vous aimerez peut-être aussi