Comparing lists items - prolog

I was wondering if you can help me in this Prolog program which should compare two lists' items and map them:
For example, I have got the two following lists:
A= [t1=v,[t2=w,[d3=a]],[d2=m,[d3=a]]]
B= [t11=v,t12=p,[t10=o,[t3=a,t22=w]],[t20=p,[t3=a,t22=m]]]
they are of the following (tentative) form:
is_plist([]).
is_plist([EL|ELs]) :-
is_el(EL),
is_plist(ELs).
is_el(_Name=_Value).
is_el(L) :-
is_plist(L).
So, I want to compare them based on the values occur after the equal signs, since I have got similar values such as v, a, m, etc. So the output would be something like
t1 = v -> t11=v
[t2=w,[d3=a]] -> [t10=o,[t3=a,t22=w]]
[d2=m,[d3=a]] -> [t20=p,[t3=a,t22=m]]
This is because the first two items of both lists got (v). The second item in the second list has different value, so it should be ignored. The second item in the first list is going to be mapped to the third one in list B since they both have w and a, etc.
I tried to split the problem into sub-problems and got the rules for getting the list’s members and collecting text after the equal signs but stuck in the comparison part. Can anyone help me in this?
%getting lists members:
m(X,[X|_]).
m(X,[_|R]) :- m(X,R).
%Collecting text rules:
c([], L, L).
c([H | T], L0, L2) :-
!,
c(H, L0, L1),
c(T, L1, L2).
c(_=C, L, [C | L]).
c(T, C) :-
c(T, [], C).

Related

Prolog : Type error: `evaluable' expected, found `[]' (an empty_list)

I am trying to delete elements from a list which satisfy a given constraint. The user inputs a budget and items having price greater than the budget are deleted. I am getting a type error when consulting the file.
:- use_module(library(lists)).
main(_) :-
write('Enter budget'),
nl,
read(Budget),
write(Buget),
numlist(1,168,MasterList),
BudgetList is [],
apply_budget(MasterList, Budget, BudgetList),
write(BudgetList).
apply_budget([],_,_).
apply_budget([H | T], B, R) :-
price(H, Pr),
(Pr > B ->
apply_budget(T, B, R);
append(R, [H], L),
R is L,
apply_budget(T, B, R)
).
You've written this entirely as if this were an imperative language. You simply can't delete elements from a list in Prolog. In Prolog everything is immutable. If you've got a list then the list cannot be changed. You can only create new things. So if you want to delete elements from a list you would create a new list without the elements you want to delete.
Here's how you should write your apply_budget predicates:
apply_budget([],_,[]).
apply_budget([H|Ts],B,Rs) :- price(H, P), P > B, !, apply_budget(Ts,B,Rs).
apply_budget([H|Ts],B,[H|Rs]) :- apply_budget(Ts,B,Rs).
The first is the case when the have an empty list in then you don't care about the budget and an empty list should come out.
The second is the case when the value H is greater than B so we throw away H and recurse. The ! (cut) is there to prevent back-tracking so that the third predicate isn't tried if we have failure.
The third is the case when H is less than or equal to B. In this case we keep H by building (not deleting) it on to the output as [H|Rs].
The cut could have been removed by writing the code this way:
apply_budget([],_,[]).
apply_budget([H|Ts],B,Rs) :- price(H, P), P > B, apply_budget(Ts,B,Rs).
apply_budget([H|Ts],B,[H|Rs]) :- price(H, P), P =< B, apply_budget(Ts,B,Rs).
I think that becomes less manageable, but you can decide which you like better.

Splitting a prolog list based on a delimiter from the list?

In Prolog, let's say I have a list such as
[fun, joke1, joke2, fun, joke3, fun, joke4, joke5, joke6]
I'm trying to build a list of lists that will result to
[ [joke1, joke2], [joke4, joke5, joke6] ]
using fun as the delimiter and ignoring a built list of length 1, hence
[joke3]
is not inside that list.
I tried using split_string but it doesn't work for what I need to obtain. I've also tried recursion using append but that didn't work out as well. Hoping I can be pointed to the right direction.
Thanks!
Here is a solution which uses two predicates:
split_on_delimiter(L, D, S) :-
split_on_delimiter_(L, D, R),
findall(X, (member(X, R), length(X,Length), Length > 1), S).
split_on_delimiter_([], _, [[]]).
split_on_delimiter_([D|T], D, [[]|T2]) :-
split_on_delimiter_(T, D, T2).
split_on_delimiter_([H|T], D, [[H|T2]|T3]) :-
dif(H, D),
split_on_delimiter_(T, D, [T2|T3]).
split_on_delimiter_/3 is the predicate which really splits the list based on the delimiter D. split_on_delimiter/3 is the predicate that you would call and which will in turn call split_on_delimiter_/3.
To keep only the sublists of length more than 1, we use findall to find all sublists of the result of the split which have Length > 1.
split_on_delimiter_/3 itself is fairly simple:
First rule: spliting an empty list results in only one sublist: the empty list.
Second rule: when the first element of the list is the delimiter, the result is the empty list followed by the result of the recursive call.
Third rule: when the first element of the list is not the delimiter (dif(H, D)), then we put that element at the beginning of the first sublist and recursive call.
An example:
?- split_on_delimiter([fun, joke1, joke2, fun, joke3, fun, joke4, joke5, joke6], fun, Z).
Z = [[joke1, joke2], [joke4, joke5, joke6]] ;
false.
Note
split_on_delimiter_/3 has extraneous choice points (which is why you can press ; after the first result, because it thinks there can be more answers but there are none). You could solve this using ! or once.
A better solution to remove those choice points is using if_/3 and (=)/3 of module(reif) (though I doubt this is useful to you):
split_on_delimiter_(L, D, [L2|T2]) :-
if_(L = [],
[L2|T2] = [[]],
( L = [H|T],
if_(H = D,
( L2 = [],
split_on_delimiter_(T, D, T2)
),
( L2 = [H|T3],
split_on_delimiter_(T, D, [T3|T2])
)
)
)
).

Prolog - member rule using concat

Can anyone explain how this member rule works using concatenation? I figured concat just returns a new list rather than seeing if a element is in the list. Concat rule is taken from my textbook.
concat([ ], L, L).
concat([H|T], L, [H|M]) :- concat(T, L, M).
member(X, L) :- concat(L1, [X | L2], L).
ex) member(a, [a, b, c]) => True
Prolog predicates usually work in different ways. Concat can be used to create a list, but it can also be used to split one list into two. One way to read the call to concat in this case is
Are there lists L1 and [X|L2] such that their concatenation is L?
Because X is the head of the second list, you know that if this statement is true, then X is a member of L.
In the example, L1 would unify with [] (i.e. all elements before a) an L2 would unify with [b, c] (all elements after a).

prolog program : compares two lists' items and return a list of unique element that are on List1

This program compares two lists' items and returns a list with items that are members of first list and are not members of second list. For example: list1=[a,b,d], list2=[r,a,f,b] ----> result =[a,b].
go:- comp([y,h,b],[b,t],R),!.
comp([],_,_) :- !.
comp(_,[],_) :- !.
comp([H|T],B,_) :- memberchk(H,B),comp(T,B,_); comp(T,B,R),write([H]).
current result is [h][y]
result I need should be [h,y]
Your request is for a predicate which returns a list with items that are members of first list and are not members of second list. But your example:
list1=[a,b,d], list2=[r,a,f,b] ----> result =[a,b]
Is the result of returns a list with members that are in both lists (intersection). I'm assuming you want what you requested, not what your example shows.
In your original, you had:
comp(_, [], _).
Which would not give a correct result if you queried, say, comp([a], [], X) since you're using the "don't care" term, _. It's an improper expression of what you probably intended, which is comp(L, [], L) (a list is itself if you exclude elements of an empty list from it). In addition, none of your original clauses instantiates a result (all of them have the "don't care" _ in that position).
A corrected version might look like this:
comp([], _, []).
comp(L, [], L).
comp([H|T], S, R) :-
( memberchk(H, S)
-> comp(T, S, R)
; R = [H|RT],
comp(T, S, RT)
).
?- comp([y,h,b],[b,t],R).
R = [y, h] ;
false.
?-
Note that the "false" response after typing ; means there are no additional solutions.

gprolog difference list with duplicate

i have to get list difference between two integer list (both ordinate).
i white this:
difference(L,[],L) :- !.
difference([],_,[]) :- !.
difference([],[],W).
difference([H|T1],[D|T2],T3) :- difference(T1,[D|T2],[H|T3]).
difference([H|T1],[H|T2],T3) :- difference(T1,T2,T3).
but why i can't get my list difference?
if i write this:
difference([],[],W):- write(X).
and this example:
| ?- difference([1,4,4],[1,4],R).
[4|_27]
it makes right!
NB if i have duplicate number i have to show it!
I find your code rather odd. For instance, your third clause: what's W for? Seems like you mean to say:
difference([],[],_).
Second problem: in the fourth clause, there's nothing stopping H and D from being independent variables with the same binding. I suspect you mean something like this:
difference([H|T1],[D|T2],T3) :- H \= D, difference(T1,[D|T2],[H|T3]).
Fixing these things seems to fix the predicate to give a reasonable looking answer:
| ?- difference([1,4,4], [1,4], R).
R = [4]
I think your first several clauses are trying to handle different sorts of base cases, is that right? E.g.:
difference(L, [], L) % handles the case where the second list is exhausted
difference([], _, []) % handles the case where the first list is exhausted
difference([], [], W) % handles the case where the lists are exhausted at the same time
One problem with this is that L = [] is a legitimate binding, so the first and third clauses mean the same thing. You can probably safely remove the third one, because it would have matched and produced the same answer on the first. The second clause is more interesting, because it seems to say that regardless of whatever work we've done so far, if the first list is empty, the result is empty. I find that possibility a bit jarring--is it possible you actually want these two base cases? :
difference([], L, L).
difference(L, [], L).
I remain unconvinced, but until I have a better idea what you're trying to accomplish I may not be able to help more. For instance, what should happen with difference([1, 4], [1, 4, 4], R)? I posit you probably want R = [4], but your code will produce R = [].
Also, I find it unlikely that
difference([],[],W):- write(X).
is going to be a helpful debugging strategy, because Prolog will generate a new variable binding for X because there's nothing for it to refer to.
The final version I have with all my changes looks like this:
difference(L, [], L) :- !.
difference([], L, L) :- !.
difference([H|T1], [D|T2], T3) :- D \= H, difference(T1, [D|T2], [H|T3]).
difference([H|T1], [H|T2], T3) :- difference(T1, T2, T3).
Edit: does this implement your requirements?
not_in1(X, Left, Right) :- member(X, Left), \+ member(X, Right).
not_in(X, Left, Right) :- not_in1(X, Left, Right).
not_in(X, Left, Right) :- not_in1(X, Right, Left).
differences(Left, Right, Differences) :-
findall(X, not_in(X, Left, Right), Differences).
?- differences([1,2,3,4], [1,3,5], X).
X = [2,4,5]
If so, I'll try to get your original code to produce answers that match.
Edit 2: OK, so the problem with the solution above is that it is O(N^2). In the worst case (two totally distinct lists) it will have to compare every item from list 1 to every item of list 2. It's not exploiting the fact that both lists are ordered (I believe that's what you mean by 'ordinate').
The result looks a lot more like your original code, but your original code is not taking advantage of the fact that the items are ordered. This is why the fourth and fifth cases are confusing looking: you should recur down one of the lists or the other depending on which number is larger. The corrected code looks like this:
differences([], Result, Result).
differences(Result, [], Result).
differences([H|Ls], [H|Rs], Result) :- differences(Ls, Rs, Result).
differences([L|Ls], [R|Rs], [L|Result]) :-
L < R,
differences(Ls, [R|Rs], Result).
differences([L|Ls], [R|Rs], [R|Result]) :-
L > R,
differences([L|Ls], Rs, Result).
You can see this produces the same result as the O(N^2) method:
?- differences([1,2,3,4], [1,3,5], X).
X = [2,4,5]
You were right, you do need both base cases. This is so the remainder of either list becomes part of the result. Presumably these will be the largest values ([5] in the example).
Now I have three inductive cases: one for <, one for > and one for =. The equality case is intuitive: recur on both lists, discarding the head of both lists. The next case basically says if the left head is less than the right head, add it to the result and recur on the left's tail. The right is unchanged in that case. The other case is the mirror of this case.
Hope this helps!

Resources