Jatin Khurana
Jatin Khurana

Reputation: 1175

Issue in Prolog Programming

Hello I am new in Prolog and trying to write small program in Prolog in order to learn it....

I have written a program to remove the positional element mean remove(List,2,Ans) will remove all the elements of position 2,4,6,8.... from the list... below is my logic

remove(L,N,Ans):-
   length(L,LEN),
   T1 is 1 mod N,
   add(L,N,[],LEN,T1,Res).

add(L,N,Ans,0,T,Ans).
add([H|T],N,Ans,LEN,0,Res):-
   LEN1 is LEN-1,
   T1 is 1 mod N,
   add(T,N,Ans,LEN1,T1,Res).
add([H|T],N,Ans,LEN,T,Res):-
   T =\= 0, LEN =\= 0,
   LEN1 is LEN-1,
   T1 is T1+1,
   T2 is T1 mod N,
   append([H],Ans,Result),
   add(T,N,Result,LEN1,T2,Res).

but every time I run it is failing when finding add. below is the trace of the program that is have for a particular instance

[trace]  ?- remove([1,2,3,4,5,6,7,8],2,X).
   Call: (6) remove([1, 2, 3, 4, 5, 6, 7, 8], 2, _G2941) ? creep
   Call: (7) length([1, 2, 3, 4, 5, 6, 7, 8], _G3039) ? creep
   Exit: (7) length([1, 2, 3, 4, 5, 6, 7, 8], 8) ? creep
   Call: (7) _G3041 is 1 mod 2 ? creep
   Exit: (7) 1 is 1 mod 2 ? creep
   Call: (7) add([1, 2, 3, 4, 5, 6, 7, 8], 2, [], 8, 1, _G3046) ? creep
   Fail: (7) add([1, 2, 3, 4, 5, 6, 7, 8], 2, [], 8, 1, _G3046) ? creep
   Fail: (6) remove([1, 2, 3, 4, 5, 6, 7, 8], 2, _G2941) ? creep
false.

Does anyone know where the problem is?

Upvotes: 1

Views: 155

Answers (2)

CapelliC
CapelliC

Reputation: 60004

Hope you can find your way in your code. I think the better way to learn a language is actually debugging it... lurker' comment already put you on the right track. But I want to show how using builtins can change your perspective (this is of course true for any language, not just Prolog).

remove(L,N,Ans) :- findall(E, (nth1(I,L,E), I mod N =\= 0), Ans).

?- remove([1,2,3,4,5,6,7,8],2,X).
X = [1, 3, 5, 7].

edit when you

Call: (7) add([1, 2, 3, 4, 5, 6, 7, 8], 2, [], 8, 1, _G3046) ? creep

no rule can be matched, specifically T cannot be a list and the number 1

add([H|T],N,Ans,LEN,T,Res):-

that said, I'm not sure how you should correct the code...

Upvotes: 1

Nicholas Carey
Nicholas Carey

Reputation: 74177

A common prolog idiom is the use of a "helper" predicate that actually does the work and carries one or more additional arguments that maintain state. In this case, we need a counter to track our position within the source list.

remove( Xs , N , Ys ) :-
  remove( Xs , 1 , N , Ys ). % Note the additional argument (our counter) initialized to 1

remove( []     , _ , _ , []     ) .  % when we exhaust the source list, we're done.
remove( [X|Xs] , P , N , [X|Ys] ) :- % otherwise,
  P mod N =\= 0 ,                    % - if the current position is NOT a multiple of N,
  P1 is P+1 ,                        % - we increment the position,
  remove(Xs,P1,N,Ys)                 % - and recurse down, adding the head of the list
  .                                  %   to the result.
remove( [_|Xs] , P , N ,    Ys  ) :- % otherwise,
  P mod N =:= 0 ,                    % - if the current position is a multiple of N,
  P1 is P1+1 ,                       % - increment the position,
  remove(Xs,P1,N1,Ys)                % - and recurse down, discarding the head of the list
  .                                  % Easy!

You might note some repetitive code. In the spirit of keeping things DRY, we can refactor it thusly:

remove( [] , _ , _ , [] ) .       % when we exhaust the source list, we're done.
remove( [X|Xs] , P , N , Ys ) :-  % otherwise,
  keep_or_discard(X,P,N,Ys,Y1) ,  % - decided whether to keep or discard the current list head
  P1 is P+1 ,                     % - increment the counter
  remove( Xs , P1 , N , Y1 )      % - and recurse down.
  .

keep_or_discard( _ , P , N , Ys , Ys ) :-    % if P is a multiple of N
  P mod N =:= 0 ,                            % - discard X,
  ! .                                        % - and eliminate the choice point.
keep_or_discard( X , _ , _ , Ys , [X|Ys] ) . % otherwise, we keep it.

Some folk dislike the use of the cut to explicitly eliminate choice points, so alternatively, we can do the refactoring via the use of prolog's implication/if-then-else operator:

remove( [] , _ , _ , [] ) .       % when we exhaust the source list, we're done.
remove( [X|Xs] , P , N , Ys ) :-  % otherwise,
  ( P mod N =:= 0                 % - if P is a multiple of N,
  ->    Y1  = Ys                  %   - then we discard the list head
  ;  [X|Y1] = Ys                  %   - otherwise, we add it to the result list
  ) ,                             % - and then ...
  P1 is P+1 ,                     % - increment the counter
  remove( Xs , P1 , N , Y1 )      % - and recurse down.
  .

Whether either of these refactorings is clearer, or easier to understand than the original is, of course, a personal choice.

Upvotes: 1

Related Questions