Lars Malmsteen
Lars Malmsteen

Reputation: 768

Evaluating functors inside a functor in Prolog

I follow the book Problem solving with Prolog by John Stobo. I've studied the Chapter 1 (Programming with Facts) and Chapter 2 (Programming with Rules) Now I am at Chapter 3: Recursion in Rules and I'm practicing the program given in the Section 3.1. I've elobarated the program a bit (without changing the main structure) and added my own functor (or function or rule ?) named is_rank_lower/2 but it doesn't work as expected.

When I enter (or ask Prolog)

is_rank_lower(ryan, jondoe).

the output is

false.

the expected output: true.

Because ryan is a private and jondoe is a corporal and private is lower in rank than corporal.

The explanations are in the code.

Question #1: How to make my own functor is_lower_rank work as expected?

Question #2: This question might be related to the book because when I write down the program exactly as it is, it works slightly wrongly and that might be causing my own functor to function wrongly, too. Just a guess.

When I enter:

lower_rank(private, corporal).

Prolog returns with true and waits at it, I have to put a dot after the true and click enter only then does it return to the ?- prompt.

The expected output is:

return with true. then return to the ?- prompt

The author seems to talk about this problem. In page 57 he writes "lower_rank would not terminae if the goal ought to fail" I've applied all the instructions but the functor still doesn't work. How to make it work?

My prolog version swi-prolog 7.2.0

% John Stobo, problem solving with Prolog, March.1989

% FACTS:
next_degree(private, corporal). 
next_degree(corporal, sergeant).
next_degree(sergeant, lieutenant).
next_degree(lieutenant, captain).
next_degree(captain, major).
next_degree(major, "lieutenant colonel").
next_degree("lieutenant colonel", colonel).
next_degree(colonel, "brigadier general").
next_degree("brigadier general", "major general").
next_degree("major general","lieutenant general").
next_degree("lieutenant general", general).

soldier(ryan, private).
soldier(jondoe, corporal).
sooldier(smartson, captain).

% RULES:
lower_rank(R1, R2) :-
    next_degree(R1, R2).
 
lower_rank(R1, R2) :- % this works but if  
    next_degree(R1, R3), % the result is "true"
    lower_rank(R3, R2). % it doesn't end properly
% only if  the user types a dot, it ends properly

is_rank_lower(A1,A2) :-
    lower_rank(soldier(A1,X), soldier(A2,X)).
% doesn't work because the functors are inserted as
% 'soldier(ryan, _G1471), soldier(jondoe, _G1471))
% not as private, corporal, i.e. they are not evaluated

Upvotes: 0

Views: 142

Answers (1)

brebs
brebs

Reputation: 4438

That next_degree/2 seems like a bizarre method - is the book suggesting it as sensible, or as an example of what not to do?

There are decent books at https://swi-prolog.discourse.group/t/useful-prolog-references/1089

This works:

% First argument is an atom, hence single quotes in swi-prolog
rank_order(private, 1).
rank_order(corporal, 2).
rank_order(sergeant, 3).
rank_order(lieutenant, 4).
rank_order(captain, 5).
rank_order(major, 6).
rank_order('lieutenant colonel', 7).
rank_order(colonel, 8).
rank_order('brigadier general', 9).
rank_order('major general', 10).
rank_order('lieutenant general', 11).

soldier(ryan, private).
soldier(jondoe, corporal).
% Not mis-spelled as "sooldier"
soldier(smartson, captain).

rank_lower(RankLower, RankUpper) :-
    rank_order(RankLower, RankLowerOrder),
    rank_order(RankUpper, RankUpperOrder),
    RankLowerOrder < RankUpperOrder.

soldier_rank_lower(SoldierLower, SoldierUpper) :-
    soldier(SoldierLower, RankLower),
    soldier(SoldierUpper, RankUpper),
    rank_lower(RankLower, RankUpper).

Results in swi-prolog:

?- rank_lower(private, corporal).
true.

?- soldier_rank_lower(ryan, jondoe).
true.

?- soldier_rank_lower(L, U).
L = ryan,
U = jondoe ;
L = ryan,
U = smartson ;
L = jondoe,
U = smartson ;
false.

2nd attempt:

rank_next(private, corporal).
rank_next(corporal, sergeant).
rank_next(sergeant, lieutenant).
rank_next(lieutenant, captain).
rank_next(captain, major).
rank_next(major, 'lieutenant colonel').
rank_next('lieutenant colonel', colonel).
rank_next(colonel, 'brigadier general').
rank_next('brigadier general', 'major general').
rank_next('major general', 'lieutenant general').
rank_next('lieutenant general', general).

soldier(ryan, private).
soldier(jondoe, corporal).
soldier(smartson, captain).

rank_lower(RankLower, RankUpper) :-
    rank_next(RankLower, RankLower1),
    % Increase lower to eventually meet with upper
    (   RankLower1 = RankUpper ;
        rank_lower(RankLower1, RankUpper)
    ).

soldier_rank_lower(SoldierLower, SoldierUpper) :-
    soldier(SoldierLower, RankLower),
    soldier(SoldierUpper, RankUpper),
    % Won't have multiple answers
    once(rank_lower(RankLower, RankUpper)).

This makes the following deterministic:

?- soldier_rank_lower(ryan, jondoe).
true.

... whilst keeping the generality of rank_lower/2, i.e.:

?- findall(L-U, rank_lower(L, U), Pairs), length(Pairs, Len).
Pairs = [private-corporal,private-sergeant,private-lieutenant, ...
Len = 66.

Upvotes: 2

Related Questions