Swi-prolog, need help defining my conditions - prolog

So I have a project, and I'm trying to create a predicate which has 3 different cases, which I will explain.
swapandp(L, P, Content, L) :-
refP(L, P, Content), !.
This is the first case where refP gives me the content of the list L on the position P, and if the content is equal to Content, it returns true and stops right there, because if the content is the same, I don't need to change anything in L.
swapandp(L, P, [Content], NewL) :-
swap(L, P, Content, NewL1),
relatedPs(P, Ps),
delNum(Content, NewL1, Ps, NewL).
This is the second case where, if Content is a one-element list, then it puts Content in the P position of the list L, and then uses a predicate i created to delete the same content in the positions related to P, which are put into a list Ps by another predicate relatedPs.
swapandp(L, P, Content, NewL) :-
swap(L, P, Content, NewL).
This is the third case where if Content is a list with more then 1 element, then it just puts Content into the position P of the list L.
but something is wrong. Because I'm probably not defining the 3 cases correctly, sorry for my english and thanks for helping.
EDIT 1:
Been trying out some things and now the first and third case are working so my problem is not the 2nd one.
I tried this:
?- swapandp([[[1,2], [1,2], [3], [1,4,5], [1,4]]], (1,1), [1], NewL).
NewL = [[[1], [1, 2], [3], [1, 4, 5], [1, 4]]].
but the expected result should be:
NewL = [[[1], [2], [3], [4, 5], [4]]].
cant figure it out.
EDIT 2:
refP(L, (Plin, Pcol), Content) :-
nth1(Plin,L,Line),
nth1(Pcol,Line,Content).
applyActionToL(L,_,[],L) :- !.
applyActionToL(Puz,Action,[P | R],N_Puz) :-
Action =.. Lst_Action,
append(Lst_Action, [L,P,Res],Actionwithargs),
Lit =.. Actionwithargs,
call(Lit),
applyActionToL(Res,Action,R,NewL).
But applyActionToL and refP were defined by my teacher so, i guess its working.
delNumAux(N, L, P, NewL) :-
refP(Puz, Pos, Content),
subtract(Content, Num, Content1),
swapandp(L, P, Content1, NewL).
delNum(N, L, Ps, NewL) :-
applyActionToL(L, delNumAux(Num), Ps, NewL).
Edit 3:
I ended up being able to fix it myself! Thanks anyway guys!

a wild guess:
swapandpropagate(L, P, [Content], OutL) :-
swap(L, P, Content, NewL),
relatedPs(P, Ps),
delNum(Content, NewL, Ps, OutL).

Related

prolog: move element between lists

I just start learning Prolog and find it's hard to handle list related problem.
If I have a list. Inside this list, I have three inner lists.
[[a,b,c], [d,e,f],[h,g]]
I need to write a predicate called "move(L, X, From, To, R)" where X is the character I want to move (this character has to be the last element in the inner list), From is the index of list I want to move from, To is the index of list I want to move to. e.g.
move([[a,b,c], [d,e,f],[h,g]], f, 2, 3, R).
returns
R = [[a,b,c], [d,e],[h,g,f]]
One more example:
move([[a,b,c], [d,e,f],[h,g]], f, 2, 1, R).
returns
R = [[a,b,c,f], [d,e],[h,g]]
I wrote a helper predicates to determine if a character is the last element in the list:
last([A], C):- A == C.
last([_|T], C):- last_one(T, C).
I spent a few hours thinking about it, but no working solutions. Any help please?
I think you must prove the following:
move(L, X, From, To, R) is the predicate you want run, so the #1 argument is a list of list (in the sample: L = [[a,b,c], [d,e,f],[h,g]] ); the #3 and #4 arguments are members of L, so you can use the predicate nth1(N, L, E), where N is the order of the element, L is the list and E the Element; each element is another list, and now you must delete the X from the list From and add it to the list To.
For it, first you can use the predicate 'select/3'; for the second, you can use 'append/3'.
I tried the folowwing rules, but it give me a different order of the main list:
move(L, X, F, T, R) :-
nth1(F, L, Lf),
nth1(T, L, Lt),
select(X, Lf, Rf),
append(Lt, [X], Rt),
select(Lf, L, Ra),
select(Lt, Ra, Rb),
append(Rb, [Rf], Rc),
append(Rc, [Rt], R).
with this code, you obtain the following:
move([[a,b,c], [d,e,f],[h,g]], f, 2, 3, R).
R = [[a,b,c], [d,e],[h,g,f]]
but:
move([[a,b,c], [d,e,f],[h,g]], f, 2, 1, R).
R = [[h,g], [d,e], [a,b,c,f]]

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.

Rotate a list in prolog recursively

I'm trying to rotate a list in prolog recursively but it does not work as expected.
Code:
rot([],[]).
rot([H|T1], [T2|H]):-rot(T1,T2).
Output:
?- rot([1,2,3], V).
V = [[[[]|3]|2]|1]
Expected output:
?- rot([1,2,3], V).
V = [3,2,1]
Could anyone explain me why my code does not work?
Since Prolog is untyped, you can indeed write something like [List|Element], but if you want a list to make sense, the only way you can construct lists is like [Element|List]. So [T2|H] does not make sense at all. In that case T2 should be an element, and H a list (or the empty list []).
You will need to define two predicates:
the main predicate (rot/2) that simply pops the head from the given list and calls the recursive predicate; and
the recursive predicate (here rot/3) that simply passes all elements of the given list and emits the original head as tail element.
Together this works like:
%main predicate rot/2
rot([],[]).
rot([H|T1],T2) :-
rot(T1,H,T2).
%recursive predicate rot/3
rot([],Last,[Last]).
rot([H|T1],Last,[H|T2]) :-
rot(T1,Last,T2).
Your code doesn't work because in an expression like [H|T], H is an element of the list and T is the tail of the list--also a list. For instance:
?- [H|T] = [1,2,3].
H = 1,
T = [2, 3].
So what happens when you switch that around?
?- [H|T] = [1,2,3], X = [T|H].
H = 1,
T = [2, 3],
X = [[2, 3]|1].
See the problem?
The problem is with the second clause. What I do is to rotate the tail of the first list inside L1 and then call append with L1 and the first element and assign the result to L (the second argument)
my-append([], L, L).
my-append([H|T], L, [H|R]) :- my-append(T, L, R).
rot([], []).
rot([H|T], L) :- rot(T, L1), my-append(L1, H, L).

Removing first occurrence in list - 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).

Prolog programs - how to make it work?

I have these two programs and they're not working as they should. The first without_doubles_2(Xs, Ys)is supposed to show that it is true if Ys is the list of the elements appearing in Xs without duplication. The elements in Ys are in the reversed order of Xs with the first duplicate values being kept. Such as, without_doubles_2([1,2,3,4,5,6,4,4],X) prints X=[6,5,4,3,2,1] yet, it prints false.
without_doubles_2([],[]).
without_doubles_2([H|T],[H|Y]):- member(H,T),!,
delete(H,T,T1),
without_doubles_2(T1,Y).
without_doubles_2([H|T],[H|Y]):- without_doubles_2(T,Y).
reverse([],[]).
reverse([H|T],Y):- reverse(T,T1), addtoend(H,T1,Y).
addtoend(H,[],[H]).
addtoend(X,[H|T],[H|T1]):-addtoend(X,T,T1).
without_doubles_21(X,Z):- without_doubles_2(X,Y),
reverse(Y,Z).
The second one is how do I make this program use a string? It's supposed to delete the vowels from a string and print only the consonants.
deleteV([H|T],R):-member(H,[a,e,i,o,u]),deleteV(T,R),!.
deleteV([H|T],[H|R]):-deleteV(T,R),!.
deleteV([],[]).
Your call to delete always fails because you have the order of arguments wrong:
delete(+List1, #Elem, -List2)
So instead of
delete(H, T, T1)
You want
delete(T, H, T1)
Finding an error like this is simple using the trace functionality of the swi-prolog interpreter - just enter trace. to begin trace mode, enter the predicate, and see what the interpreter is doing. In this case you would have seen that the fail comes from the delete statement. The documentation related to tracing can be found here.
Also note that you can rewrite the predicate omitting the member check and thus the third clause, because delete([1,2,3],9001,[1,2,3]) evaluates to true - if the element is not in the list the result is the same as the input. So your predicate could look like this (name shortened due to lazyness):
nodubs([], []).
nodubs([H|T], [H|Y]) :- delete(T, H, T1), nodubs(T1, Y).
For your second question, you can turn a string into a list of characters (represented as ascii codes) using the string_to_list predicate.
As for the predicate deleting vovels from the string, I would implement it like this (there's probably better solutions for this problem or some built-ins you could use but my prolog is somewhat rusty):
%deleteall(+L, +Elems, -R)
%a helper predicate for deleting all items in Elems from L
deleteall(L, [], L).
deleteall(L, [H|T], R) :- delete(L, H, L1), deleteall(L1, T, R).
deleteV(S, R) :-
string_to_list(S, L), %create list L from input string
string_to_list("aeiou", A), %create a list of all vovels
deleteall(L, A, RL), %use deleteall to delete all vovels from L
string_to_list(R, RL). %turn the result back into a string
deleteV/2 could make use of library(lists):
?- subtract("carlo","aeiou",L), format('~s',[L]).
crl
L = [99, 114, 108].
while to remove duplicates we could take advantage from sort/2 and select/3:
nodup(L, N) :-
sort(L, S),
nodup(L, S, N).
nodup([], _S, []).
nodup([X|Xs], S, N) :-
( select(X, S, R) -> N = [X|Ys] ; N = Ys, R = S ),
nodup(Xs, R, Ys).
test:
?- nodup([1,2,3,4,4,4,5,2,7],L).
L = [1, 2, 3, 4, 5, 7].
edit much better, from ssBarBee
?- setof(X,member(X,[1,2,2,5,3,2]),L).
L = [1, 2, 3, 5].

Resources