Search code examples
prolog

What would be an elegant/idiomatic way to introduce a predicte on a list of atoms?


Let's say I have objects a1,a2,...,a{n} and I want to introduce effectively an order on those objects like:

gt(a1,a2).
gt(a2,a3).
...
gt(a{n-1},a{n}).

Now {n} would be a large number like 200. Instead of writing this out as 199 facts. Is there a "better" way to do this in Prolog?

I'm thinking of something in the spirit of defining a list and then applying gt to a zip of that list and a shift of it.

If that's totally against how Prolog is supposed to be used and would have to bend over backwards for such a solution then I'd totally accept a short explanation as to why that is as the answer.


Solution

  • What about putting them in a list in order smaller to bigger, and a grammar which describes their relation in the list:

    cards([ace, two, three, four, five, six, seven,
           eight, nine, ten, jack, queen, king]).
    
    % see https://www.metalevel.at/prolog/dcg "any sequence at all"
    ... --> [] | [_], ... .
    
    gt_([Small, Big]) --> ..., [Small], ..., [Big].
    
    gt(Big, Small) :-
        cards(Cards),
        phrase(gt_([Small, Big]), Cards, _).
    

    Since this consumes the list once, it's slightly more algorithmically clean, but perhaps less clear and with more overhead than the following:

    cards([ace, two, three, four, five, six, seven,
           eight, nine, ten, jack, queen, king]).
    
    gt(Smaller, Bigger) :-
        cards(Cards),
        nth1(Sindex, Cards, Smaller),
        nth1(Bindex, Cards, Bigger),
        Bindex > Sindex.
    

    Both leave choicepoints and both can be used to generate answers to queries like "what X is queen larger than?".

    The following version pairs up the elements in the list with a list of indices doesn't leave choicepoints, but can't generate answers:

    cards([ace, two, three, four, five, six, seven,
           eight, nine, ten, jack, queen, king]).
        
    gt(Smaller, Bigger) :-
        cards(Cards),
        length(Cards, Len),
        numlist(1, Len, Counts),
        pairs_keys_values(Pairs, Cards, Counts),
        memberchk(Smaller-Sindex, Pairs),
        memberchk(Bigger-Bindex, Pairs),
        Bindex > Sindex.
    

    These last two do a simple comparison of numbers, so for something like queen > ace it avoids having to trace the whole transitive relation queen > jack > ten > nine > ... two > ace, but then it does have to scan the list multiple times.

    You could instead put them in a SWI Prolog dictionary to get a faster lookup, but a non-standard syntax. Or loop over them assertzing the information into the Prolog database, which seems to be not recommended, or easier to write some code once to write out the 199 facts for you and then copy-paste that into your code. (Perhaps as:

    % card(name, value)
    card(ace, 1).
    card(two, 2).
    ...
    card(king, 13).
    

    and then querying twice and comparing the values.