Search code examples
prologinfinite-loopclpfdfailure-slice

List of integers and infinite loop in Prolog CLPFD


Suppose I want to represent integers like so: integer:Sign:[FirstDigit,SecondDigit,...]. For instance, 42 would be represented as integer:positive:[4,2].

I need a predicate that generates the value of the integer based on this representation and vice versa.

Here is what I came up with:

integer_value_('integer':Sign:[H],E) :-
    H in 0..9,
    (
        Sign = 'positive',
        E #= H
        ;
        Sign = 'negative',
        E #= -H
    ).
integer_value_('integer':Sign:[H,I|T],E) :-
    H in 0..9,
    length([I|T],L),
    (
        Sign = 'positive',
        E #= F + H * 10^L
        ;
        Sign = 'negative',
        E #= F - H * 10^L
    ),
    integer_value_('integer':Sign:[I|T],F).

This works as expected. However, it has the unfortunate property of accepting things like integer:positive:[0,1], that is, leading zeroes at the beginning of the list. This is especially problematic when I enumerate all possible integers using integer_value_(I,J), label([J]).: the ones with leading zeroes also show up.

I then attempted to correct this by using integer_value_ only for all but the first digit, and using integer_value for the first one (keeping in mind that we need to accomodate for 0 being represented with a list containing only 0):

integer_value('integer':Sign:[H],E) :-
    abs(E) #< 10,
    abs(E) #> -1,
    integer_value_('integer':Sign:[H],E).
integer_value('integer':Sign:[H,I|T],E) :-
    H in 1..9,
    length([I|T],L),
    (
        Sign = 'positive',
        E #= F + H * 10^L
        ;
        Sign = 'negative',
        E #= F - H * 10^L
    ),
    integer_value_('integer':Sign:[I|T],F).

However now it does not behave properly. For example, integer_value(I,-19). returns I = integer:negative:[1, 9], but if we ask for another answer Prolog goes into an infinite loop for reasons I don't understand (it should say false, or already know there are no other answers).

This problem does not occur with the "opposite" query integer_value(integer:negative:[1,9],Z). which returns Z = 19 and then false, nor does it occur when both arguments are Variables (it enumerates numbers properly, with no leading zeroes), which is surprising to me.

Any idea what that infinite loop occurs, and if there's an easy way to fix it?


Solution

  • To see the problem, it is sufficient to look at a tiny fraction of your program. In fact the following is sufficient:

    integer_value('integer':Sign:[H],E) :- false,
        abs(E) #< 10,
        abs(E) #> -1,
        integer_value_('integer':Sign:[H],E).
    integer_value('integer':Sign:[H,I|T],E) :-
        H in 1..9,
        length([I|T],L), false,
        (   Sign = 'positive',
            E #= F + H * 10^L
            ;
            Sign = 'negative',
            E #= F - H * 10^L
        ),
        integer_value_('integer':Sign:[I|T],F).
    

    L occurs here for the first time, so any length is possible. You will have to modify the length-goal somehow.