Search code examples
prolog

Check an integer in range as prolog with state transition


I would like to check if an integer is within a range in Prolog, as in this question: Prolog: Check if X is in range of 0 to K - 1 However, I would like to be able to reason about the range. This code works:

check_age(X, Mn, Mx)  :-
   X >= Mn,
   X <= Mx.

so that check_age(5, 3,8). responds correctly. I would like to be able to do

check_age(5, Y, 8).

and have it return Y=5. That is, determine what Mn should be to make it true. I am currently using swi-prolog.


Solution

  • CLPFD will do it, but it will tell you that Y in inf..5 because it's also true if Y=4; Y=3; Y=2; ... negative infinity.

    :- use_module(library(clpfd)).
    
    n_min_max(N, Min, Max) :-
        (Min #=< N), (N #< Max).
    
    ?- n_min_max(5, Y, 8).
    Y in inf..5
    

    You would need to label Y, and choose some limits for it, and perhaps tune the CLPFD search strategy so that Min takes the biggest possible value and Max takes the smallest possible value:

    ?- n_min_max(5, Y, Z),
       [Y, Z] ins 0..100,
       labeling([max(Y), min(Z)], [Y,Z]).
    
    Y = 5,
    Z = 6 ;
    
    Y = 5,
    Z = 7 ;
    
    Y = 5,
    Z = 8 ;
    

    That will change your approach to the code in ways that might be annoying.

    Otherwise, an ugly chain of if/else checking what is grounded (which variables have values and which don't):

    n_min_max(N, Min, Max) :-
        ((ground(Min), ground(Max))
        -> (Min =< N, N < Max)
        ;  ((ground(N), ground(Min))
           -> N = Max
           ; ((ground(N), ground(Max))
             ->   N = Min
             ; (ground(N)
               ->  (N = Min, N=Max)
               ; false)))).
    

    e.g.

    ?- n_min_max(5, 3, 8).
    true
    
    ?- n_min_max(5, Y, 8).
    Y = 5
    
    ?- n_min_max(5, 3, Z).
    Z = 5
    
    ?- n_min_max(5, Y, Z).
    Y = Z, Z = 5
    

    and you could change the third line to use between/3 to generate possibles for X on backtracking, nb. between/3 includes the top value of the range

        -> (succ(RangeMax, Max), between(Min,RangeMax,N))
    

    with that change:

    ?- n_min_max(X, 5, 8).
    X = 5 ;
    X = 6 ;
    X = 7