Search code examples
prologfunctor

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

Solution

  • 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.