views:

152

answers:

3

Okay, my last prolog question. It's the common geneaology problem.

I am suppose to take a list of facts and have a function called descendant that will return a list that has all the descendants. For example:

Given the rules:

parent('Bob', 'Tim').
parent('Joe', 'Bob').

The function call:

    descendant('Joe', X).

should return:

        X = ['Bob', 'Tim'].

I can get it to return the immediate descendant of 'Joe' but not the full line. Here's what I have.

 % Recursive case
  descendant(X,DList) :- parent(X,A), NewDList = [A|DList], 
                         descendant(A, NewDList).
 % Base case, I have a feeling this is wrong.
  descendant(_,[]).

This code only seems to return true or false, or just an empty [].

I could use some help on what I might need to look at. Thanks.

A: 

My Prolog is a bit rusty and I'm loathe to post an answer to such a problem - you won't learn much that way.

I'll just point out that you shouldn't have that assignment statement in there - NewDList = [A|DList] - this is considered bad form in the Prolog style of programming - assignments should only be used where there is not a "pure" logical solution - certainly not the case here.

Cheers, Craig.

nowhere_fast
A: 
parent('Bob', 'Tim').
parent('Joe', 'Bob').

descendant(X,[H|T]) :- parent(X,H), descendant(H, T).
descendant(X,[]) .

returns

?- descendant('Joe', L).
L = ['Bob', 'Tim'] ;
L = ['Bob'] ;
L = [].

actually it is hard to write predicate that will return only ['Bob', 'Tim'] because list ['Bob'] is also valid. if you decide to leave only longest chain it gets too comlicated

if i understood question incorrectly here is one version:

desc(X, A) :- parent(X,H), desc(H, A).
desc(X, A) :- X = A.
Andrey
I don't think this works if somebody has more than one direct descendant, for instance if Joe is also the parent of Mary.
Michael Williamson
did you test it? if not please test then critique. i just tested it. i works fine. i you are SURE that it doesn't work at certain data please give a proof
Andrey
The example I gave doesn't work -- if I add parent('Joe', 'Mary'), then the descendants of Joe are still listed as Bob and Tim (although Mary is given as a further result when backtracking)
Michael Williamson
i just added your rule and result is L = ['Bob', 'Tim'] ;L = ['Bob'] ;L = ['Mary'] ;L = [].which is expected
Andrey
From the question, I would expect the result to be L = ['Bob', 'Tim', 'Mary']
Michael Williamson
A: 

Firstly, we'll create a predicate that can finds a single descendant.

descendant(X, Y) :- parent(X, Y).
descendant(X, Y) :- parent(X, Z), descendant(Z, Y).

We can then use the findall predicate to list all descendants:

descendants(X, L) :- findall(A, descendant(X, A), L).

So, for instance:

parent(bob, tim).
parent(joe, bob).
parent(joe, mary).

descendant(X, Y) :- parent(X, Y).
descendant(X, Y) :- parent(X, Z), descendant(Z, Y).

descendants(X, L) :- findall(A, descendant(X, A), L).

gives:

?- descendants(joe, X).
X = [bob, mary, tim].
Michael Williamson
Well the problem of having someone say having 6 children, I would want it to list all 6 as descendants. However, the rules given only have people having one child which allows the implementation audrey gave acceptable. But I would like to know how to do that in case it came up later on a test. I tried this one, but all it returns is the direct descendant, am I doing something wrong?
poorStudent
Are you sure `descendants` (with an s) is being used, rather than `descendant` (which unifies one person with a descendant)?
Michael Williamson
Yea, what's happening is it will output X = [bob]I have to push ; for it to step and put out X = [mary] and so on until it errors out.
poorStudent
What version of Prolog are you using? I'm using SWI Prolog 5.8.
Michael Williamson
SWI Prolog 5.8.3
poorStudent