Prolog - removing elements from sublists

844 Views Asked by At

Problem statement: You're given a list containing integers and lists of integers. You must remove from each sublist, the 1st, 2n, 4th, 8th.. etc, element.

My solution

domains
        list=integer*
        elem=i(integer);l(list)
        clist=elem*

predicates
        modify(list, list, integer, integer)
        exec(clist, clist)
clauses
        modify([], [], _, _).
        modify([H|T], Mod, I, P):-
                P=I,
                !,
                I1=I+1,
                P1=P*2,
                modify(T, Mod, I1, P1).
        modify([H,T], [H|Mod], I, P):-
                I1=I+1,
                modify(T, Mod, I1, P).

        exec([], []).
        exec([i(N)|T], [i(N)|LR]):-
                exec(T, LR).
        exec([l(L)|T], [l(Mod)|LR]):-
                modify(L, Mod, 1, 1).

        do():-
                exec([i(1),l([1,2,3,4,5,6,7,8,9]),l([1,2,3,4])],X),
                write(X).

The problem is that the algorithm works until it removes the 1st and 2nd element from each sublist, but from then on doesn't remove a thing and I'm not sure what I'm doing wrong.

the exec predicate is used to assert whether the current element is an integer or a list of integers, add the integer to the result, or add the modified list to the result.

the modify predicate modifies a given list and should remove all elements on position power of 2.

I wrote the do predicate just for calling it as a goal, to avoid writing the list every time I want to test it.

2

There are 2 best solutions below

1
CapelliC On

I think you're doing P1=P*2 too often, then you mismatch successive powers of 2

also, you have a typo here

modify([H,T], [H|Mod], I, P):- ...

should read

modify([H|T], [H|Mod], I, P):- ...

I would write

modify([], [], _).
modify([_|T], Mod, I):-
    is_pow2(I), !, % as noted by SQB
    I1 is I+1,
    modify(T, Mod, I1).
modify([H|T], [H|Mod], I):-
    I1 is I+1,
    modify(T, Mod, I1).

To keep is_pow2/1 simple, you could do

is_pow2(1).
is_pow2(2).
is_pow2(4).
is_pow2(8).
is_pow2(16).
...

or use your Prolog arithmetic facilities. In SWI-Prolog a simple minded definition could be

is_pow2(N) :- 
    between(0, 63, L),
    N is 1 << L.
0
SQB On

Actually, the core of your modify is correct, so it may a problem in exec. The following works for me:

do_modify(X) :-
  modify([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18], X, 2).

modify(Lin, Lout, Base) :- modify(Lin, Lout, Base, 1, 1).

modify([], [], _, _, _).
modify([_|T], X, Base, N, Power) :-
  N = Power,
  !,
  P1 is Power * Base,
  N1 is N + 1,
  modify(T, X, Base, N1, P1).
modify([H|T], [H|X], Base, N, Power) :-
  N1 is N + 1,
  modify(T, X, Base, N1, Power).

Tested it on http://www.compileonline.com/execute_prolog_online.php.