Inserting into a list of lists prolog - 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.

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 do I make this Prolog query work in a predicate?

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.

How do I change position of two elements in a list(PROLOG)

predicate change_pos(E1, E2,Lin,Lout).
The Lin has any number of elements, and I need to change all occurences of E1 to E2, and vice-versa. And return in Lout.
I was thinking to do something like this:
change(X, Y, [], []).
change(X, Y, [X|L], [Y,L1]):- change(X,Y,L,L1).
change(X, Y, [Z|L], [Z,L1]:- X \== Z, change(X,Y,L,L1).
But this way is not swiping two number of the list
I'm supposing, since this is homework, it's an exercise to learn list processing and recursion. But in Prolog, a common tool for processing each term in turn in a list is maplist:
% Rule for changing one element
change_element(X, Y, X, Y).
change_element(X, Y, Y, X).
change_element(X, Y, Z, Z) :- dif(X, Z), dif(Y, Z).
% Rule for changing a list
change(X, Y, L1, L2) :-
maplist(change_element(X, Y), L1, L2).
Which yields:
?- change(a, b, [a,b,c,b,a], L).
L = [b, a, c, a, b] ? ;
no
?-
For a determinate solution, you can use if_/3:
change1(X, Y, A, B) :-
if_(=(Y, A), B = X, A = B).
change2(X, Y, A, B) :-
if_(=(X, A), B = Y, change1(X, Y, A, B)).
change(X, Y, L1, L2) :- maplist(change2(X, Y), L1, L2).
Which yields:
?- change(a, b, [a,b,c,b,a], L).
L = [b, a, c, a, b].
?-
You're almost there. Your base case (the empty lists) and your second rule (swap X for Y) are basically fine (apart from the details pointed out in the comments). However, you are missing a rule for vice-versa (swap Y for X). And in your last rule you likely want to make sure that Z differs not only from X but also from Y, otherwise Z would be subject to rule two or three.
change(X, Y, [], []).
change(X, Y, [X|L], [Y|L1]) :-
change(X,Y,L,L1).
change(X, Y, [Y|L], [X|L1]) :- % <- vice versa case
change(X,Y,L,L1).
change(X, Y, [Z|L], [Z|L1]) :-
dif(X,Z), % <- neither X=Z
dif(Y,Z), % <- nor vice versa
change(X,Y,L,L1).
Here are some example queries. What does [1,2,3,4] look like after swapping 1 with 2 and vice versa?
?- change(1,2,[1,2,3,4],L).
L = [2,1,3,4] ? ;
no
What did [2,1,3,4] look like before swapping 1 with 2 and vice versa?
?- change(1,2,L,[2,1,3,4]).
L = [1,2,3,4] ? ;
no
Which elements have been swapped in [1,2,3,4] if the resulting list is [2,1,3,4] ?
?- change(X,Y,[1,2,3,4],[2,1,3,4]).
X = 1,
Y = 2 ? ;
X = 2,
Y = 1 ? ;
no

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).

substitute in a nested list (prolog)

/* substitute(X,Y,Xs,Ys) is true if the list Ys is the result of substituting Y for all occurrences of X in the list Xs.
This is what I have so far:
subs(_,_,[],[]).
subs(X,Y,[X|L1],[Y|L2]):- subs(X,Y,L1,L2).
subs(X,Y,[H|L1],[H|L2]):- X\=H, not(H=[_|_]), subs(X,Y,L1,L2).
subs(X,Y,[H|_],[L2]):- X\=H, H=[_|_], subs(X,Y,H,L2).
My code works except it omits the elements following the nested list. For example:
?- subs(a,b,[a,[a,c],a],Z).
Z = [b, [b, c]] .
What should I add to this program?
Here is how you could write it using (... -> ... ; ...):
subs(_, _, [], []).
subs(X, Y, [H1|T1], [H2|T2]) :-
(H1 == X ->
H2 = Y
; is_list(H1) ->
subs(X, Y, H1, H2),
subs(X, Y, T1, T2)
;
H1 = H2,
subs(X, Y, T1, T2)
).
The problem is that once you find a nested list, you forget about whatever is behind that nested list. Instead, after recursing with the nested nest, simply continue as before. Thus, you should change the last clause as follows:
subs(X,Y,[H|L1],[H2|L2]):- X\=H, H=[_|_], subs(X,Y,H,H2), subs(X, Y, L1, L2).
Aside from that, there are a couple of ways in which you can improve the code:
Use cuts (!/0) to stop backtracking. In this way you don't have to repeat yourself.
You can use is_list/1 to test whether an argument is a list.
It's okay to use more spaces. Really.
So, an alternative solution is (now using \+/1 instead of not/1):
subs(_, _, [], []).
subs(X, Y, [X|T1], [Y|T2]) :- subs(X, Y, T1, T2), !.
subs(X, Y, [H|T1], [H|T2]) :- \+ is_list(H), subs(X, Y, T1, T2), !.
subs(X, Y, [H1|T1], [H2|T2]) :- subs(X, Y, H1, H2), subs(X, Y, T1, T2).
Demonstration:
?- subs(a, b, [a, [a, [d, f, a]], a, b, a, [g]], Z).
Z = [b, [b, [d, f, b]], b, b, b, [g]].

Resources