Prolog using examples – Part 4

In this part of this practical tutorial, we are going to do some coding involving facts, rules, recursion, and lists.

Example 1: Obtain the head of the list, obtain the tail of the list.

/* Get the head of the list */
get_head(Head, [Head|_]).

/* Get the tail of the list */
get_tail(Tail, [_|Tail]).

?- get_head(Head_is, [a,b,c,d]).
Head_is = a.

?- get_tail(Tail_is, [a,b,c,d]).
Tail_is = [b, c, d].

Example 2: Find out if an element is a member of a list.

/* Base case - We find the element inside the list */
/* The element is found if the head of the list can be unified with the element we are searching */
member(Element, [Element|_]).

/* Recursive function that call member with the tail without taking care of the head of the list */
/* The idea is that the base case will be called and check if the element is found */
member(Element, [_|Tail]) :-
    member(Element, Tail).    

?- member(a, [a,1,3,b,c]).
true .

/* Using OR ';', we ask Prolog to search for another element in the list */
?- member(a, [a,1,a,b,c]).
true ;
true ;
false.

/* Element is not found in the list */
?- member(a, [c,1,3,b,c]).
false.

Example 3: Find an specific element member inside a list in a determined position.

/* Base case */
/* The element was found in the list was found, set Position to 1 to stop recursion */
get_member_at(1, [Head|_], Head).

/* Recursive case */
/* Go thought the list until position is less than 1 indicating that member was found */
get_member_at(Position, [_|Tail], Element):-
    Position > 1,
    Temp_Position is Position - 1,
    get_member_at(Temp_Position, Tail,  Element).

/* Get the member at position 3 of the list */
?- get_member_at(3, [1,3,5,7], Lst).
Lst = 5. 

/* This fails when trying to get an element out or bounds of the list */
?- get_member_at(5, [1,3,5,7], Lst).
false. 

/* BE CAREFUL, Sometimes prolog can behave weird base on how you use your functors eg.*/

/* The follow is when prolog create dynamic variables because doesn't now the limit of the list */
?- get_member_at(5, Lst, Lst).
Lst = [_G455, _G458, _G461, _G464, **|_G468].

/* In this case, not only produce dynamic variables to be instanciated, but also add
the list [1,a,b] as the fifth head element of the list */
?- get_member_at(5, Lst, [1,a,b]).
Lst = [_G473, _G476, _G479, _G482, [1, a, b]|_G486] ;

Example 4: Lets obtain the numbers of elements that belong to a simple list.

/* Base case - When the list is empty, unify with zero */
number_of_elements([], 0).

/* Recursive case */
number_of_elements([_|Tail], Number_counted) :-
    number_of_elements(Tail, counter),
    Number_counted is counter + 1.

In the recursive case, Prolog will call number_of_elements passing the tail of the list until the list is empty. Then, for every time number_of_elements was call it would return the counter + 1 to the previous call. At the end, we will obtain the total number of elements in the list.

?- number_of_elements([a,1,b,2,^, &], Counted).
Counted = 6.

/* In this case the sublist [1,2,3] inside the list is considerate an element as a whole */
?- number_of_elements([a,b,c,[1,2,3]], Counted).
Counted = 4.

?- number_of_elements([[a,b,c,1,2,3]], Counted).
Counted = 1.

If inside the code we would had the unification symbol instead of the word ‘is’, we would have a different result:

/* Base case - When the list is empty, unify with zero */
number_of_elements([], 0).

/* Recursive case */
number_of_elements([_|Tail], Number_counted) :-
    number_of_elements(Tail, counter),
    Number_counted is counter + 1.

?- number_of_elements([a,1,b,2,^, &], Counted).
Counted = 0+1+1+1+1+1+1.

Example 5: Lets sort a list of elements

/* Base case - Empty list */
sort_list([]).

/* Base case - [_] indicate we don't case about the element, there is only one, the list is sorted */
sort_list([_]).

/* Recursion case */
sort_list([First_element, Second_element|Tail]):-
    First_element =< Second_element,
    sorted([Second_element|Tail]).

?- is_list_sorted([1,2,3,4]).
true .

?- is_list_sorted([1,2,3,4]).
true ;
false.

?- is_list_sorted([1,3,3,4]).
true.

?- is_list_sorted([1,3,5,4]).
false.

This topic will continue in the next posting.

Share
Leave a comment

Prolog using Examples – Part 2

Lets begin with cases related with recursion:

In an imperative style,  you would use a loop to produce a result, while in an functional style, it is common to use recursion to produce the same result.

The most common example related with recursion is the factorial recursion, in which for example the factorial of 6 (6! = 5 * 4 * 3 * 2 * 1) would give us

/* Base case of the factorial, when N is 0 then return 1 */
factorial(0, 1).

factorial(N, M) :-
    N1 is N - 1,
    factorial(N1, M1),
    M is N * M1.
?- factorial(6, T).
T = 720;
ERROR: Out of local stack

Note: As you can see, when we added the OR ‘;’, prolog tried to find another  answer and went out of bound.
To prevent this we can do an small modification as follows:

/* Base case of the factorial, when N is 0 then return 1 */
factorial(0, 1).

factorial(N, M) :-
    N > 0
    N1 is N - 1,
    factorial(N1, M1),
    M is N * M1.
?- factorial(6, T).
T = 720;
False

In this case, when we tell Prolog that we wish to find another possible answer, Prolog would indicate false since it cannot find any other possible solution.

Lets go over some examples:

Example 1: We have a family and we wish to know the descendent of some individual.

/* Maria is_mother_of Pedro */
is_mother_of(Maria, Pedro).

/* Susana is_mother_of Maria */
is_mother_of(Susana, Maria).

/* Pepe is_father_of Pedro */
is_father_of(Pepe, Pedro).

/* Diego is_father_of Pepe */
is_father_of(Diego, Pepe).

/* Person is_parent_of Child if Person is_mother_of Child */
is_parent_of(Person, Child):- is_mother_of(Person, Child).

/* OR */

/* Person is_parent_of Child if Person is_father_of_Child */
is_parent_of(Person, Child):- is_father_of(Person, Child) */

/* Begin RECURSION */

/* Recursion Base */

/* Person is_decendent_of Adult if Adult is_parent_of Person */
is_decendent_of(Person, Adult) :- is_parent_of(Adult, Person).

/* Recursion */
/* Person is_decendent_of Adult if Older(person) is_parent_of Person AND Older is_decendent_of Adult */
 is_decendent_of(Person, Adult) :- is_parent_of(Older, Person), is_decendent_of(Older, Adult).

Example 2: Fibonacci (http://en.wikipedia.org/wiki/Fibonacci_number) is an integer sequence such as 0, 1, 1, 2, 3, 5, 8… As you may notice, the first two numbers are 0 and 1, an the subsequence is form by a number created from the sum of the two previous numbers: 0 + 1 = 1 (0,1,1) , 1 + 1 = 2 (1,1,2), 1 + 2 = 3 (1,2,3), 2 + 3 = 5 (2,3,5) …

/* Recursion base case */
fibonacci(0, 0).

/* Recursion base case */
fibonacci(1, 1).

/* Recursion */
fibonacci(N, F) :-
    N1 is N - 1,
    N2 is N - 2,
    fibonacci(N1, F1),
    fibonacci(N2, F2),
    F is F1 + F2.

?- fibonacci(8, T).
T = 21;
ERROR: Out of local stack

Again in this case, when asking prolog to find another answer, it would go out of bounds because there is no case that stop it to keep calling itself. We need to modify this program:

/* Recursion base cases */
fibonacci(0, 0).
fibonacci(1, 1).

/* Recursion */
fibonacci(N, F) :-
    N > 0,
    N1 is N - 1,
    N2 is N - 2,
    fibonacci(N1, F1),
    fibonacci(N2, F2),
    F is F1 + F2. ?- fibonacci(8, T).
T = 21;
False

Again, Prolog couldn’t find a different answer when we ask it to search for it and let it know by returning false after the OR ‘;’.

Example 3: The following code is the Ackermann’s predicate (http://en.wikipedia.org/wiki/Ackermann_function).
One of the purposes to doing this kind of function could be to test the limits of the compiler.
The Ackermann’s functions is defined as follows:
ackermann( m,n ) =    n + 1                                                             if m = 0
ackermann( m,n ) =    ackermann(m – 1, 1)                                if n = 0 and m > 0
ackermann( m,n ) =    ack( m-1, ackermann( m, n-1 ) )          if n >0 and m > 0

/* ackermann(m, n) = n + 1 if m = 0 */
ackermann(Mi, Ni, No):- Mi = 0, No is Ni + 1.

/* ackermann(m, n) = ackermann(m - 1, ackermann(m, n - 1)) if n > 0 and m > 0 */
ackermann(Mi, Ni, No):-
    Mi > 0, Ni > 0,
    Mi2 is Mi - 1, Ni2 is Ni - 1,
    ackermann(Mi, Ni2, No2),
    ackermann(Mi2, No2, No).

/* ackermann(m, n) = ackermann(m - 1, 1) if n = 0 and m > 0 */
ackermann(Mi, Ni, No):-
    Mi > 0, Ni = 0,
    Mi2 is Mi - 1, Ni2 is 1,
    ackermann(Mi2, Ni2, No).

In the next blog posting, we are going to see Lists in Prolog.

Share
Leave a comment