5
votes

I need to write a program in Prolog that should remove every second element of a list. Should work this: [1,2,3,4,5,6,7] -> [1,3,5,7]

so far I have this, but it just returns "false".

r([], []). 
r([H|[T1|T]], R) :- del(T1,[H[T1|T]], R), r(R).

del(X,[X|L],L).
del(X,[Y|L],[Y|L1]):- del(X,L,L1).
5
You seem to have a typo in the second clause for r/2 where it calls del/3, in that the second argument is not a proper term. Also you then call a one-argument predicate r, where presumably you meant to call the two-argument r/2. By the way, a more concise way to write [H|[T1|T]] is [H,T1|T].hardmath

5 Answers

8
votes

This is pretty much Landei's answer in specific Prolog syntax:

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

The second predicate is not required.

4
votes

Alternative solution using foldl/4:

fold_step(Item, true:[Item|Tail], false:Tail).
fold_step(_Item, false:Tail, true:Tail).

odd(List, Odd) :-
    foldl(fold_step, List, true:Odd, _:[]).

Usage:

?- odd([1, 2, 3, 4, 5, 6, 7], Odd).
Odd = [1, 3, 5, 7]

The idea is to go through the list, while keeping "odd/even" flag and flipping its value (false -> true, true -> false) on each element. We also gradually construct the list, by appending those elements which have "odd/even" flag equal to true, and skipping others.

3
votes

This fine answer by @code_x386 utilizes and foldl/4.

Let's use only one fold_step/3 clause and make the relation more general, like so:

fold_step(X, [X|Xs]+Ys, Ys+Xs).

list_odds_evens(List, Odds, Evens) :-
   foldl(fold_step, List, Odds+Evens, []+[]).

Sample queries:

?– list_odds_evens([a,b,c,d,e,f], Odds, Evens).
Evens = [b,d,f], Odds = [a,c,e]
?– list_odds_evens([a,b,c,d,e,f,g], Odds, Evens).
Evens = [b,d,f], Odds = [a,c,e,g]

Edit

Why not use one clause less and do away with predicate fold_step/3? to the rescue!

:- use_module(library(lambda)).

list_odds_evens(List, Odds, Evens) :-
   foldl(\X^([X|Xs]+Ys)^(Ys+Xs)^true, List, Odds+Evens, []+[]).
2
votes

Another possibility is to use DCGs, they are usually a worthwhile consideration when describing lists:

list_oddindices(L,O) :-
   phrase(oddindices(L),O).  % the list O is described by oddindices//1

oddindices([]) -->           % if L is empty
   [].                       % O is empty as well
oddindices([X]) -->          % if L has just one element
   [X].                      % it's in O
oddindices([O,_E|OEs]) -->   % if L's head consists of at least two elements
   [O],                      % the first is in O
   oddindices(OEs).          % the same holds for the tail

This is certainly less elegant than the solutions using foldl/4 but the code is very easily readable, yet it solves the task described by the OP and works both ways as well:

?- list_oddindices([1,2,3,4,5,6,7],O).
O = [1, 3, 5, 7] ;
false.

?- list_oddindices(L,[1,3,5,7]).
L = [1, _G4412, 3, _G4418, 5, _G4424, 7] ;
L = [1, _G4412, 3, _G4418, 5, _G4424, 7, _G4430] ;
false.
0
votes

I have no Prolog here to try it out, and I got a little bit rusty, but it should be along the lines of

r([]) :- [].
r([X]) :- [X].
r([X,Y|Z]) :- R=r(Z),[X|R].

[Edit]

Of course pad is right. My solution would work in functional languages like Haskell or Erlang:

--Haskell
r [] = []
r [x] = [x]
r (x:_:xs) = x : (r xs)

In Prolog you have to "pull" the right sides into the argument list in order to trigger unification.