1

Given a query such as:

containsN(4,2,Z).

I should get: Z = [2,2,2,2].

or

containsN(4,W,[3,3,3,3])

I should get: W = 3.

So in other words, for the first example I need 4 instances of 2 in a list bound to Z.

For the second example I need the element in the list applied 4 times bound to W.

My attempt so far results in an infinite loop:

containsN(Y,W,Z) :- contains_helper(Y,W,Z).

contains_helper(0,_,_).
contains_helper(Y,W,[H|T]) :- W = H, Y0 is Y - 1, contains_helper(Y0,W,T).

The idea is, I call a helper function so that I can check the elements of Z one by one. When the counter reaches 0 I know the list is true because for each iteration H = W. However, I'm getting an infinite loop.

user2962883
  • 57
  • 1
  • 8
  • 1
    Your code seems to work pretty well (assuming `fill_helper` is `contains_helper`). What input causes infinite loop? – Artur Malinowski Dec 05 '14 at 07:40
  • When you reach `0`, you also know that the list should be empty. There's no reason for the helper predicate. `W = H` can be ommited by moving the unification in the clause head: `contains_helper(Y,W,[W|T]):-…`. – Patrick J. S. Dec 05 '14 at 07:42
  • Furthermore, you should check if `Y` is greater than 0 (and maybe cut afterwards). Alternatively, cut in your base case (list empty/counter 0). This fixes your loop issues. – Patrick J. S. Dec 05 '14 at 07:54
  • Yes fill_helper was supposed to be contains_helper. Sorry. Fixed. – user2962883 Dec 05 '14 at 08:01
  • I've implemented your suggestions, Patrick. The problem is now that I'm getting an empty list shown instead of the desired 'output'. I.e. first two examples. – user2962883 Dec 05 '14 at 08:02
  • Let me clarify, sorry: I added the empty list condition, put the W in the clause head, and checked whether Y was greater than 0. – user2962883 Dec 05 '14 at 08:03

1 Answers1

2

If you want the predicate to be as general as possible (and I renamed it to repeat/3)

?- repeat(X, N, L).
N = 0, L = [] ;
N = 1, L = [X] ;
...

You can use the nice properties of length/2, like this:

% True when L is a list with N repeats of X
repeat(X, N, L) :-
    length(L, N),
    maplist(=(X), L).

Here, length(L, N) can check for the list's length, or generate a list of the desired length, or generate lists of increasing length upon backtracking.

The maplist(=(X), L) will succeed if each element of the list L unifies with the variable X. You could have written it as:

foo([], _).
foo([X|Xs], X) :-
    foo(Xs, X).

but I don't think this is necessary.