Search code examples
listprologmaskingclpfd

Masking in Prolog


I have recently been trying to figure out Prolog and been messing with lists of lists in Prolog. I am trying to create a sort of mask I suppose in p Prolog. I have a predicate that determines the difference between two lists of lists (L1 and L2 lets say) in Prolog and saves them as a list of a list(Lets say R). I have another predicate that simply states if the difference is equal to zero(noDifference). I would like to have two resulting lists of lists (M1 and M2) based off of L1 and L2 compared to the R. For example I would like to compare all values of L1 and L2 to R, if a negative value is at a location of R then the value in the same location of L1 is saved into M1. And if a positive value is at a location of R then the value in the same location of L2 is saved into M2 if that makes sense. Before all of this I check with my noDifference function to see if the difference is 0 and if so all values of M1 and M2's lists of lists will be 0.

This is what I have so far(I'm not sure if I started it right)

masker(L1,L2,R,M1,M2):- noDifference(R1), M1=R, M2=R1;

and for the rest of it here are what some example values should look like under the hood

L1=[[1,5,3,8],[1,5,3,8]]
L2=[[5,4,7,4],[5,4,7,4]]
R=[[4,-1,4,-4],[4,-1,4,-4]]
M1=[[0,5,0,8],[0,5,0,8]]Neg values of L1 at R are stored rest are 0)
M2=[[5,0,7,0],[5,0,7,0]](Pos values of L2 at R are stored rest are 0)

Any insight if what I am doing so far is right and how to properly formulate the subgoals/where I should go next would be awesome!

edit with ex predicate

?- masker([[1,5,3,8],[1,5,3,8]],
          [[5,4,7,4],[5,4,7,4]],
          [[4,-1,4,-4],[4,-1,4,-4]], M1, M2).
M1=[[0,5,0,8],[0,5,0,8]].
M2=[[5,0,7,0],[5,0,7,0]].

Solution

  • Think what your predicate should describe. It is a relation between five lists of lists which, according to the example you provided, are of same length. This suggests the base case with five empty lists. Otherwise the heads of all five lists are lists themselves, that are in a specific relation to each other, let's call it lists_mask_mlists/5. And of course the same should be true for the tails, which can be realized by a recursive goal. So your predicate masker/5 could look something like that:

    masker([],[],[],[],[]).
    masker([X|Xs],[Y|Ys],[M|Ms],[R1|R1s],[R2|R2s]) :-
       lists_mask_mlists(X,Y,M,R1,R2),
       masker(Xs,Ys,Ms,R1s,R2s).
    

    The actual masking relation also has a base case with five empty lists. Otherwise there are two further cases:

    1) The current masking element (head of the third list) is negative: The head of the first list is the head of the fourth list and the head of the fifth list is 0

    2) The current masking element is positive: The head of the second list is the head of the fifth list and the head of the fourth list is 0

    You can express that like so:

    lists_mask_mlists([],[],[],[],[]).
    lists_mask_mlists([X|Xs],[_Y|Ys],[M|Ms],[X|R1s],[0|R2s]) :-   % 1)
       M < 0,
       lists_mask_mlists(Xs,Ys,Ms,R1s,R2s).
    lists_mask_mlists([_X|Xs],[Y|Ys],[M|Ms],[0|R1s],[Y|R2s]) :-   % 2)
       M >= 0,
       lists_mask_mlists(Xs,Ys,Ms,R1s,R2s).
    

    With this predicate your example query yields the desired result:

       ?- masker([[1,5,3,8],[1,5,3,8]],[[5,4,7,4],[5,4,7,4]],[[4,-1,4,-4],[4,-1,4,-4]],M1,M2).
    M1 = [[0,5,0,8],[0,5,0,8]],
    M2 = [[5,0,7,0],[5,0,7,0]] ? ;
    no
    

    Note however, that due to < and >= this only works, if the third list is variable free. Replacing the first 4 in the third argument by a variable yields an instantiation error:

       ?- masker([[1,5,3,8],[1,5,3,8]],[[5,4,7,4],[5,4,7,4]],[[X,-1,4,-4],[4,-1,4,-4]],M1,M2).
         ERROR at  clause 2 of user:masked/5 !!
         INSTANTIATION ERROR- =:=/2: expected bound value
    

    If you intend to use the predicate with a third argument that is not variable free, you might like to consider using clpfd. Include the line

    :-use_module(library(clpfd)).
    

    in your source file and alter lists_mask_mlists/5 like so:

    lists_mask_mlists([],[],[],[],[]).
    lists_mask_mlists([X|Xs],[_Y|Ys],[M|Ms],[X|R1s],[0|R2s]) :-
       M #< 0,                                                    % <- here
       lists_mask_mlists(Xs,Ys,Ms,R1s,R2s).
    lists_mask_mlists([_X|Xs],[Y|Ys],[M|Ms],[0|R1s],[Y|R2s]) :-
       M #>= 0,                                                   % <- here
       lists_mask_mlists(Xs,Ys,Ms,R1s,R2s).
    

    Now the second query works too:

       ?- masker([[1,5,3,8],[1,5,3,8]],[[5,4,7,4],[5,4,7,4]],[[X,-1,4,-4],[4,-1,4,-4]],M1,M2).
    M1 = [[1,5,0,8],[0,5,0,8]],
    M2 = [[0,0,7,0],[5,0,7,0]],
    X in inf.. -1 ? ;
    M1 = [[0,5,0,8],[0,5,0,8]],
    M2 = [[5,0,7,0],[5,0,7,0]],
    X in 0..sup ? ;
    no