How do I make this Prolog query work in a predicate? - prolog

I am trying to make this Prolog query:
placeRedBlocks(4, X), findall(Y, loopReplace(X, Y), A).
which outputs this:
A = [[r, r, r, b], [r, r, r, r], [b, r, r, r]],
X = [b, b, b, b]
work in the code if I only type in
placeRedBlocks(4, X).
The code I am using:
printList([ ]).
printList([H|T]) :- print(H), nl, printList(T).
placeRedBlocks(Length, List) :-
findall('b', between(1, Length, _), List).
replace([_|T], 0, X, [X|T]).
replace([H|T], I, X, [H|R]):-
I > -1,
NI is I-1,
% write([H|T]),
replace(T, NI, X, R), !.
% replace(L, _, _, L).
placeRedBlockUnit(A,_,0,_,A):- !.
placeRedBlockUnit(Line,Index,NumElm,Elm,NLine) :-
replace(Line,Index,Elm,BLine),
Index1 is Index+1,
NumElm1 is NumElm-1,
placeRedBlockUnit(BLine,Index1,NumElm1,Elm,NLine).
loopReplace(ListToReplace, NewList) :-
length(ListToReplace, Len),
TotalCount is Len-3,
between(0, TotalCount, Iterations),
between(3, Len, Size),
placeRedBlockUnit(ListToReplace, Iterations, Size, 'r', NewList).
Unfortunately, if I change placeRedBlocks to this it doesn't work.
placeRedBlocks(Length, List) :-
findall('b', between(1, Length, _), List),
findall(Y, loopReplace(List, Y), _).
and I only get:
X = [b, b, b, b]
What is happening here?
Is it possible for it to return back to the same list?

I realized that as long as I use a variable twice in a predicate I don't need to make it a parameter to my predicate.
Changing this
placeRedBlocks(Length, List) :-
findall('b', between(1, Length, _), List)
to this
placeRedBlocks(Length, List3) :-
findall('b', between(1, Length, _), List),
findall(Y, loopReplace(List, Y), List2),
append([List], List2, List3).
made me able to use the queries in the predicate while also keeping the predicate at 2 parameters.

Related

Lists size multiplication

I'm new to Prolog and I'm trying to get my head around lists. The problem I'm struggling with is:
Given numbers in the form of lists (1 : [x], 3: [x, x, x]), implement the 'times' predicate /3.
E.g.: times([x, x], [x, x, x], R).
R = [x, x, x, x, x, x].
The plus, and successor predicates where 2 previous points of the exercise. I know I'm not using the successor predicate, but it didn't seem that useful later on.
This is what i've tried so far
successor([], [x]).
successor([X|T], R) :-
append([X|T], [X], R).
plus(L1, L2, R) :- append(L1, L2, R).
times([], _, []).
times(_, [], []).
times([_], L, L).
times(L, [_], L).
times([_|T], L2, R) :- plus(L2, R, RN),
times(T, L2, RN).
The output is:
R is [].
I think you make things too complicated here. You can define successor as:
successor(T, [x|T]).
We can define plus/3 as:
plus([], T, T).
plus([x|R], S, [x|T]) :-
plus(R, S, T).
This is more or less the implementation of append/3, except that here we check if the first list only contains x.
For times/3 we know that if the first item is empty, the result is empty:
times([], _, []).
and for a times/3 where the first item has shape [x|R], we need to add the second item to the result of a call to times/3 with R:
times([x|R], S, T) :-
times(R, S, T1),
plus(S, T1, T).
So putting it all together, we obtain:
successor(T, [x|T]).
plus([], T, T).
plus([x|R], S, [x|T]) :-
plus(R, S, T).
times([], _, []).
times([x|R], S, T) :-
times(R, S, T1),
plus(S, T1, T).

How can I reverse list and replace all the specified values with other value?

Imagine that we have a list: [1,2,2,3,4]. The problem can be splitted into two parts:
Reverse the list (we should receive [4,3,2,2,1])
Replace, e.g. all 2 with 100, so we should receive [4,3,100,100,1]
It's easy to reverse the list, here's a working code:
simple_reverse(List, Rev) :-
simple_reverse(List, Rev, []).
simple_reverse([], L, L).
simple_reverse([H|T], L, SoFar) :-
simple_reverse(T, L, [H|SoFar]).
But I have some troubles with replacing the elements. I have tried the following approach:
reverse(a, b, List, Rev) :-
reverse(a, b, List, Rev, []).
reverse(a, b, [], L, L).
reverse(a, b, [H|T], L, SoFar) :-
reverse(a, b, T, L, [H|SoFar]).
reverse(a, b, [a|T], L, SoFar) :-
reverse(a, b, T, L, [b|SoFar]).
What is the problem? By the way, I'm using https://swish.swi-prolog.org/ to run the code.
Instead of just a,b (which are atoms) you need to place variables in reverse/4 and reverse/5 predicates:
reverse(A, B, List, Rev) :-
reverse(A, B, List, Rev, []).
reverse(_, _, [], L, L).
reverse(A, B, [H|T], L, SoFar) :-
dif(A,H), %case where H is not A so we skip it
reverse(A, B, T, L, [H|SoFar]).
reverse(A, B, [A|T], L, SoFar) :-
reverse(A, B, T, L, [B|SoFar]).
You can use foldl in conjonction with library(lambda) :
:- use_module(library(lambda)).
my_reverse(L, A, B, R) :-
foldl([A,B] +\X^Y^Z^(dif(X, A) -> Z = [X |Y]; Z = [B|Y]), L, [],R).
example :
?- my_reverse([1,2,2,3,4], 2, 100, R).
R = [4, 3, 100, 100, 1].

Inserting into a list of lists prolog

I am working on a roster system project in prolog, and am using a modified search and insert structure based on an example provided by our professor. However, when I try to run my implementation of it, the provided record list is never inserted into the roster, even if there are no other elements present. I am not very familiar with prolog, and this issue is throwing me for a loop.
My implementation of the search/insert method is as follows:
search(_, [], 0).
search([X,_,_], [[X, _, _]|_], 1).
search([_,Y,_], [[_, Y, _]|_], 1).
search([X,Y,Z], [[_, _, _]|T], C) :- search([X, Y, Z], T, C).
searchinsert([X, Y, Z], L, L) :- search([X, Y, Z], L, 1).
searchinsert([X, Y, Z], L, [[X, _, _] | L]) :- search([X, Y, Z], L, 0).
searchinsert([X, Y, Z], L, [[_, Y, _] | L]) :- search([X, Y, Z], L, 0).
As designed, it should reject the provided record list if either the first or second element match that of another element in the roster. The function is called as follows:
process(4, X) :-
nl,
write('\tInsert student into roster.'), nl,
read_student_info([A, B, C]),
nl, nl, searchinsert([A, B, C], X, X).
Maybe just change the last call:
..., searchinsert([A, B, C], X, 1).
so X has a chance to be instantiated to a list. But you should rethink the 'data flow' of your process/2 driver, likely it's missing an argument.

How to split a list filled with logic variables in prolog?

I want to write a predicate split(List, Pivot, Result) holds when Result is a list of sublists that List divided by Pivot. For example split([A, B, '#', C, '#', D], '#', [[A, B], [C], [D]]) is true. Note that the elements are lists with logic variables.
I asked a question about how to do this to lists which do not have logic variables as elements. Here's the answer:
split(L,P,R):-split(L,P,[],R).
split([],_,[],[]).
split([],_,S,[S]) :- S \= [].
split([P|T],P,[],R) :- split(T,P,[],R).
split([P|T],P,L,[L|R]) :- L \= [], split(T,P,[],R).
split([H|T],P,S,R) :- H \= P, append(S, [H], S2), split(T,P,S2,R).
However this does not work with the lists which have logic variables as elements. Any suggestions to solve this problem? Thank you!
You were very nearly there.
Consider this slightly modified version:
split(L, P, R) :- split(L, P, [], R).
split([], _P, [], []) :- !.
split([], _P, Acc, [Acc]).
split([X|Xs], P, Acc, Result) :-
X \== P, !,
append(Acc, [X], NewAcc),
split(Xs, P, NewAcc, Result).
split([_X|Xs], P, [], Result) :- !, split(Xs, P, [], Result).
split([_X|Xs], P, Acc, [Acc|Result]) :- split(Xs, P, [], Result).
This implementation relies on the accumulator as you'd tried, but simply shunts the accumulator into the result list as soon as the list item doesn't match the pivot using \==/2.

delete some elements from a list

What I want to do is to delete part of a list specified in another list i.e. e.g.
?- deleteSome([1,4,3,3,2,2],[1,2,4],Z).
Z = [3,3,2].
I first defined the following. No problem there.
deleteOne(X, [X|Z], Z).
deleteOne(X, [V|Z], [V|Y]) :-
X \== V,
deleteOne(X,Z,Y).
Then, the following does not work as expected.
deleteSome([], [], _).
deleteSome([X|Xs], Y, Zs) :-
deleteSome(Xs, Y, [X|Zs]).
deleteSome([X|Xs], Y, Zs) :-
member(X,Y),
deleteOne(X,Y,Y),
deleteSome(Xs, Y, Zs).
I would use the powerful select/3 builtin
deleteSome(L, D, R) :-
select(E, L, L1),
select(E, D, D1),
!, deleteSome(L1, D1, R).
deleteSome(L, _, L).
test:
?- deleteSome([1,4,3,3,2,2],[1,2,4],Z).
Z = [3, 3, 2].
I must admit, I don't understand your deleteSome code at all. Here's what I'd do (no Prolog here, so might contain errors):
deleteSome(X, [], X).
deleteSome(X, [Y|Ys], Z) :-
deleteOne(Y, X, T),
deleteSome(T, Ys, Z).
I.e. If there's nothing to delete, no change. Otherwise, the result is when we delete the first of the to-deletes, and then delete the rest of them.
There is some confusion in that it seems your deleteOne has (Original, ToDelete, Result) parameters, but deleteSome has (ToDelete, Original, Result). For consistency, I'd rather rewrite it so the signatures are compatible:
deleteSome([], Y, Y).
deleteSome([X|Xs], Y, Z) :-
deleteOne(X, Y, T),
deleteSome(Xs, T, Z).

Resources