简体   繁体   中英

Prolog Recursion removing elements at index that is multiples of n from a list where n is any number

This is my first time asking a question here but I have a problem that I really can't wrap my head around which is Prolog recursion especially when it deals with list. So the task that I am supposed to solve is to write a drop predicate that works like this. For example, drop([1,2,3,4,5,6,7,8,9], 2, L) where L = [1,3,5,7,9] and N=n where elements at position n, 2n, 3n.... will be removed. The list starts from 1 is another thing to be noted.

Here is my attempt so far and thought process:

drop([], _, []).

indexOf([X|_], X, 1). %Using 1 because the question says the first element starts from 1.

indexOf([_|Ys], Y , I):-
    indexOf(Ys, Y, N),
    I is N + 1.

drop([X|Xs], Y, [X|_]) :-
    indexOf([X|Xs] , X , A),
    Z is A mod Y,
    Z \== 0.

drop([X|Xs], Y, Zs) :-
    %indexOf([X|Xs], X, A),
    drop(Xs, Y, Zs).

I created an indexOf predicate to find the index of the elements starting from 1 . Next, my idea was to use the my first drop recursive case (in the code above it is the 5th case) to check and see whether the position of the element returns a remainder of zero when divided by the Y (second input). if it does not return a remainder of zero, then the X remains inside the list and is not dropped. Then, prolog moves on to the 2nd drop recursive case which can only be arrived when Z=0 and it will drop X from the list to return Z s. In essence, an element with index n, 2n, 3n... that is returned by indexOf will be dropped if it does not return a remainder of zero when divided by Y (second input).

I have not learnt Cut at this point of the course at the moment. I would appreciate if someone can point me to the right direction. I have been working on this for almost a day.

I am still trying to adapt the logic and declarative thinking in this programming paradigm. I would appreciate it if you could share with me, how did you personally go about mastering Logic programming?

First, looking at your approach, there's a flaw with using the indexOf/3 . That is, at a given point in time when you need to know the index of what you're removing, you don't know what the item is yet until you get to it. At that point, the index is 1 .

That's one issue with the following rule:

drop([X|Xs], Y, [X|_]) :-
    indexOf([X|Xs], X, A),
    Z is A mod Y,
    Z \== 0.

The first subquery: indexOf([X|Xs], X, A) will succeed with A = 1 on its first attempt, just by definition (of course, X has index 1 in list [X|Xs] . As it succeeds, then the next line Z is A mod Y yields 1 since 1 mod Y is always 1 if Y > 0 . And therefore, Z \\== 0 will always succeed in this case.

Thus, you get the result: [X|_] where X is the first element of the list. So the first solution you get for, say, drop([1,2,3,4], 2, L). is L = [1|_] . Your third drop/3 predicate clause just recurses to the next element in the list, so then it will succeed the second clause the same way, yielding, L = [2|_] , and so on...

Starting from the top, here's a way to think about a problem like this.

Auxiliary predicate

I know I want to remove every N -th element, so it helps to have a counter so that every time it gets to N I will ignore that element. This is done with an auxiliary predicate, drop/4 which will also have a recurring counter in addition to the original N :

drop(L, N, R) :-
    drop(L, N, 1, R).   % Start counter at 1

Base rule

If I drop any element from the empty list, I get the empty list . It doesn't matter what elements I drop. That's expressed as:

drop([], _, _, []).

You have this rule expressed correctly already. The above is the 4-argument version.

Recursive rule 1 - The N -th element

I have list [X|Xs] and X is the N -th element index, then the result is R if I skip X , reset my index counter to 1 , and drop the N -th element from Xs :

drop([_|Xs], N, N, R) :-   % I don't care what the element is; I drop it
    drop(Xs, N, 1, R).

Recursive rule 2 - Other than the N -th element

I have list [X|Xs] and X is the A -th element (< N ), then the result is [X|R] if I increment my index counter ( A ), and drop N -th elements from Xs with my updated index counter :

drop([X|Xs], N, A, [X|R]) :-
    A < N,
    NextA is A + 1,
    drop(Xs, N, NextA, R).

Those are all the needed rules (4 of them).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM