Removing first occurrence in list - prolog - prolog

I need to remove only one occurrence in the list. Actually doesn't matter if it's first or last. One match needs to be removed.
I'm having trouble understanding why the following doesn't work as intended.
deleteOne(_,[],[]).
deleteOne(Term, [Term|Tail], Result) :-
deleteOne(Term, [], [Result|Tail]), !.
deleteOne(Term, [Head|Tail], [Head|TailResult]) :-
deleteOne(Term, Tail, TailResult), !.
Output
41 ?- deleteOne(5,[2,3,1,5,2,3,1],X).
X = [2, 3, 1, 5, 2, 3, 1].
It works when I replace term with an empty String or some random String.
deleteOne(Term, [Term|Tail], Result) :-
deleteOne("", Tail, Result), !.
Output
41 ?- deleteOne(5,[2,3,1,5,2,3,1],X).
X = [2, 3, 1, 2, 3, 1].
But I don't think this is the best solution for many reasons. Not for my current problem, but for example longer lists. Or if a list contains empty String - don't know if this is possible in Prolog.
Why wont the first example work? And what other solutions are there?

Your first one doesn't work because this doesn't make much sense:
deleteOne(Term, [Term|Tail], Result) :-
deleteOne(Term, [], [Result|Tail]), !.
That means the result of the next one has to have the current result as its head.
An better solution would be this:
delete_one(_, [], []).
delete_one(Term, [Term|Tail], Tail).
delete_one(Term, [Head|Tail], [Head|Result]) :-
delete_one(Term, Tail, Result).
If you want it to be determinative, add a cut on the second clause. As is, it can do this:
?- delete_one(2, [1, 2, 3, 1, 2, 3], X).
X = [1,3,1,2,3] ? ;
X = [1,2,3,1,3] ? ;
X = [1,2,3,1,2,3] ? ;
no

To delete only the first occurrence of an item X from a list L.Here I used cut operation.
delete(X,[X|T],T):-!.
delete(X,[Y|T],[Y|T1]):-delete(X,T,T1).

Related

Prolog - Why does my definition for append_list not return the combined list?

% appends an element to the beginning of a list.
append_element(X, T, [X|T]).
% append a list to another list to create a combined list,
% by breaking the first list apart, and using append_element.
append_list([], L, L).
append_list([H|T], L, NewList) :-
append_element(H, L, NL),
append_list(T, NL, NL).
When I try to run append_list,
?- append_list([1,2], [3, 4, 5], NL).
I get back false. Instead of
NL = [2, 1, 3, 4, 5].
Why?

Prolog - dividing list into n-elements sections

I have a predict which gets first N elements:
nfirst(N, _, Lnew) :- N =< 0, Lnew = [].
nfirst(_, [], []).
nfirst(N, [X|Y], [X|Y1]) :- N1 is N - 1, nfirst(N1, Y, Y1).
It works:
% nfirst(3,[1,2,3,4,5,6],X).
% X = [1, 2, 3]
I need a predict for divide list like below:
% divide([a,b,c,d,e,f,g,h],[3,2,1,2],X).
% X = [[a,b,c],[d,e],[f],[g,h]]
The best way is using nfirst.
Very similar question to the one I answered here. Again, the trick is to use append/3 plus length/2 to "bite off" a chunk of list, per my comment above:
split_at(N, List, [H|[T]]) :- append(H, T, List), length(H, N).
If you run that, you'll see this:
?- split_at(4, [1,2,3,4,5,6,7,8], X).
X = [[1, 2, 3, 4], [5, 6, 7, 8]] ;
So this is the backbone of your program, and now you just need the usual recursive stuff around it. First, the base case, which says, if I'm out of list, I should be out of split locations, and thus out of result:
divide([], [], []).
Note that explicit base cases like this make your program more correct than something like divide([], _, _) because they will cause you to fail if you get too many split locations for your list size.
Now the recursive case is not difficult, but because split_at/3 puts two things together in a list (probably a bad choice, you could make split_at/4 as an improvement) you have to take them out, and it clouds the logic a bit here while making (IMO) a nicer API on its own.
divide(List, [Split|Splits], [Chunk|Rest]) :-
split_at(Split, List, [Chunk, Remainder]),
divide(Remainder, Splits, Rest).
This should be fairly straightforward: we're just taking a Split location, using it to chop up the List, and repeating the processing on what's left over. It seems to work as you expect:
?- divide([a,b,c,d,e,f,g,h],[3,2,1,2],X).
X = [[a, b, c], [d, e], [f], [g, h]] ;
false.
Hope this helps! Compare to the other answer, it may illuminate things.

prolog implement custom flatten

Hello I have a list of list in prolog and I want to flatten them. I've made a preidacate that flatten lists as I wanted but I have this case:
[[2,2,3],[3,2]] to be flattened like this: [2,2,3,0,3,2]
i.e., I want to add a 0 to the new list if the last element of the previous list is the same as the first element of the next list. Can you help me?
here is my work so far:
myflat([],[]) :- !.
myflat([H|T],Z) :- myflat(H,K), myflat(T,L), append(K,L,Z),!.
myflat(H,[H]) :- not(H = [K]).
but I cannot think how to check the equality of elements stated above
Not perfect but works:
myflat([[A,B|List]|ListOfLists], [A|Output]) :-
myflat([[B|List]|ListOfLists], Output).
myflat([[A],[A|List]|ListOfLists], [A,0|Output]) :-
myflat([[A|List]|ListOfLists], Output).
myflat([[A],[B|List]|ListOfLists], [A|Output]) :-
A =\= B,
myflat([[B|List]|ListOfLists], Output).
myflat([[A]], [A]).
Sample input/output:
?- myflat([[1,2,1],[1,2],[2,1]], X).
X = [1, 2, 1, 0, 1, 2, 0, 2, 1] .
?- myflat([[2,2,3],[3,2]],X).
X = [2, 2, 3, 0, 3, 2] ;
false.

Predicate to filter non constants from a list

I'm working on a predicate only_atoms/2(List+, Result-) that I'd like to filter non atoms.
For example :
only_atoms([1, 2, X, h(Y), 'aba'], Result).
should return
Result = [1, 2, 'aba'].
I do not care about the order.
Here is the piece of code I came up with :
only_atoms([], []) :- !.
only_atoms([Head | Tail], [Head | Result]) :-
atom(Head),
!,
only_atoms(Tail, Result).
only_atoms([_ | Tail], Result) :-
only_atoms(Tail, Result).
I thought this was the right kind of reasoning to handle such a problem but seem to be wrong since it yields me [](edit : it actually yields [aba], see precisions below, my bad !) no matter what. I'd appreciate some help !
A first hint: for 1 and 2, atom returns false.
By the way, I was looking for the filter predicate, in the standard library it happens to be called include, it's usually better if you use what the language already provides ;-)
?- include(atom, [1, 2, X, h(Y), 'aba'], Result).
Result = [aba].
or if you wanted just to filter out variables:
?- exclude(var, [1, 2, X, h(Y), 'aba'], Result).
Result = [1, 2, h(Y), aba].
Another by the way, one curious difference between your only_atoms and using include(atom, ...) is that yours will unify variables in the first list with atoms in the second list, whereas the include won't.
?- only_atoms([1, x, 2, Y], [x, y]).
Y = y.
?- include(atom, [1, x, 2, Y], [x, y]).
false.
Those subtleties of Prolog always astonish me (I guess that's because I didn't pay enough attention at the university xD).
You probably need to force the Head not to be an atom on the alternate clause, otherwise it is an option for atoms as well.
This returns Result = ['aba'] for me.
only_atoms([], []).
only_atoms([Head | Tail], [Head | Result]) :- atom(Head), !, only_atoms(Tail, Result).
only_atoms([Head | Tail], Result) :- \+atom(Head), !, only_atoms(Tail, Result).
Alternatively, you could try using findall/3.
atoms_list(List, Result) :- findall(Item, (member(Item, List), atom(Item)), Result).

prolog lists and list manipulation

i am trying to write a binary predicate to take one list, compute mod 5 for each element and then put it in another list. so far, i have done this,
mod5(X,L):- R = [], modhelper(R,L), write(R).
modhelper(X,L):- memb(E,L), mod2(E,Z), addtolist(Z,X,X), modhelper(X,L).
%Get an element from the list L.
memb(E,[E|_]).
memb(E,[_|V]):- memb(E,V).
%If element is integer, return that integer mod 5 else return as is.
mod2(N,Z):- isInt(N) -> Z is N mod 5 ; Z = N.
%add this modified element to the output list.
addtolist(Y,[],[Y]).
addtolist(Y,[H|T],[H|N]):- addtolist(Y,T,N).
memb,mod2, addtolist work as expected but I'm doing something wrong in modhelper which I'm not able to figure out.
Any help is appreciated.
In SWI-Prolog:
mod5(X, Y) :-
Y is X mod 5.
apply_mod5_to_list(L1, L2) :-
maplist(mod5, L1, L2).
Usage:
?- apply_mod5_to_list([2, 4, 6, 8], L2).
L2 = [2, 4, 1, 3].
?- apply_mod5_to_list([2, 4.1, 6, 8], L2).
ERROR: mod/2: Type error: `integer' expected, found `4.1'
?- apply_mod5_to_list([2, not_number, 6, 8], L2).
ERROR: is/2: Arithmetic: `not_number/0' is not a function
You can easily modify this code if you want a slightly different behavior, e.g. if you want to tolerate non-integers (why do you want that btw?).
In case you cannot use maplist, you can implement it yourself, at least a more specialized version of it, e.g. something like this:
partition_the_list_into_first_and_rest([X | Xs], X, Xs).
% The result on an empty list is an empty list
apply_mod5_to_list([], []).
% If the input list contains at least one member
apply_mod5_to_list(L1, L2) :-
partition_the_list_into_first_and_rest(L1, X, Xs),
call(mod5, X, Y),
partition_the_list_into_first_and_rest(L2, Y, Ys),
apply_mod5_to_list(Xs, Ys).
To this code you can still apply a lot of syntactic simplification, which you should probably do to turn it into an acceptable homework solution...

Resources