Prolog Subtract List Unification - prolog

I am trying to subtract one list from another in prolog. In my program the input list have blank spaces in them (e.g. [1,2,_,4])
I am getting the following output:
?- subtract([1,2,3,4],[3,4,_],L).
L = [2].
when I want my output to be
L = [1,2].
So my question is how can I prevent the blank spaces from unifying with other elements? Have been stuck on this for a while.

Assuming you want the "blank spaces" to be ignored, you can simply make a version of each list with those removed and compute their difference:
listWOblanks( [], [] ).
listWOblanks( [H|T], Tx ) :- var(H), !, listWOblanks( T, Tx ).
listWOblanks( [H|T], [H|Tx] ) :- listWOblanks( T, Tx ).
If, when the first list has a blank and the second does not, you need the result to still have a blank, you could modify the above to add a 3rd argument that tells you if any blanks were removed so you can correct the difference accordingly. I believe SWI-Prolog has a predicate, ground, which will tell you if a term has no variables in it, which would do the job w/o needing to modify listWOblanks.

larsmans is correct, the _ is an anonymous variable, and the definition of lists:subtract/3 (which I'm assuming you're using in SWI-Prolog) will always unify them to ground list members because of it's definition using memberchk/2.
If you want subtract behaviour where variables are to be treated like ground terms, then you can redefine it like this:
subtract2([], _, []) :- !.
subtract2([A|C], B, D) :-
var_memberchk(A, B), !,
subtract2(C, B, D).
subtract2([A|B], C, [A|D]) :-
subtract2(B, C, D).
Note that subtract2/3 here is nearly the same as the definition of lists:subtract/3 (try listing(subtract). to see for yourself). The only difference is the list membership predicate, var_memberchk/2, which is defined like this:
var_memberchk(A0, [A1|_]) :-
A0 == A1, !.
var_memberchk(A0, [_|R]) :-
var_memberchk(A0, R).
This checks to see if a variable, atom or term is in the list. So, trying this we get:
?- subtract2([1,2,3,4],[3,4,_],L).
L = [1, 2].
Note that it still works if we name the variables, as you'd expect:
?- subtract2([1,2,A,3,B,4],[3,A,4],L).
L = [1, 2, B].
It also works if we explicitly give names to anonymous variables, like this:
?- subtract2([1,2,_A,3,_B,4],[3,_A,4],L).
L = [1, 2, _B].
Finally, note that since _ doesn't have a name, subtract2/3 will never be able to match it to other anonymous variables in either list, for example:
subtract2([1,2,_,4],[3,_,4],L).
L = [1, 2, _G415].
Where _G415 is the anonymous global variable denoted by the _ in the first input list. The second is a different global variable (like _G416, for instance), so could never match the anonymous variable in the first list.

Another way:
% Uses list catenation to generate sublists /subtraction
conc([], L, L).
conc([X|L1], L2, [X|L3]) :-
conc(L1, L2, L3).
% Finds all list members that have values and then
% use list catenation to generate the sublists
subtract(L1, L2, L3) :-
findall(D, (nth0(N, L2, D), nonvar(D)), PureL2),
conc(L3, PureL2, L1).
This assumes that only one list has '_', but you could do the same findall for L1 if both lists have the same problem.

Related

Adding to a list of lists in Prolog

I am currently attempting to write a Prolog program which will add a given character to the end of a list. The list's I want to append are elements within a list. This is what I currently have.
extends(X, [], []).
extends(X, [[Head]|Lists], Y):-
append([X], [Head], Y),
extends(X, Lists, [Y]).
Here I'm attempting to concatenate X and Head, storing it in Y. However I want Y to be a list of lists, so when it repeats the process again the next concatenation will be stored also in Y. So at the end of the program Y would store the results of all the concatenations. I would want the result to look like as follows.
?- extends(a, [[b,c], [d,e,f], [x,y,z]], Y).
Y = [[b,c,a], [d,e,f,a], [x,y,z,a]].
Could anyone help me out with this?
You want to apply some operation to corresponding elements of two lists. That operation talks about lists itself. It's easy to get confused with the nested levels of lists, so let's try not to think in those terms. Instead, define first a predicate that does the extension of one list:
element_list_extended(Element, List, Extended) :-
append(List, [Element], Extended).
This behaves as follows, using cases from your example:
?- element_list_extended(a, [b, c], Extended).
Extended = [b, c, a].
?- element_list_extended(a, List, [x, y, z, a]).
List = [x, y, z] ;
false.
Looks good so far. All we need to do is to apply this operation to corresponding elements of two lists:
extends(_Element, [], []).
extends(Element, [Xs | Xss], [Ys | Yss]) :-
element_list_extended(Element, Xs, Ys),
extends(Element, Xss, Yss).
And this works:
?- extends(a, [[b,c], [d,e,f], [x,y,z]], Y).
Y = [[b, c, a], [d, e, f, a], [x, y, z, a]] ;
false.
The key to making it work was to decompose the problem into two parts and to solve those simpler parts separately.
Now, if we like, since the definition of element_list_extended/3 is a single clause containing a single goal, we might decide to do without it and inline its definition into extends/3:
extends(_Element, [], []).
extends(Element, [Xs | Xss], [Ys | Yss]) :-
append(Xs, [Element], Ys),
extends(Element, Xss, Yss).
As you can see, you were quite close! You just had some superfluous brackets because you got confused about list nesting. That's precisely where decomposing the problem helps.
(As the other answer said, SWI-Prolog has some useful libraries that allow you to express even this in even shorter code.)
extends(PostFix, ListIn, ListOut) :-
maplist({PostFix}/[In,Out]>>append(In,[PostFix],Out),ListIn, ListOut).
This is using library(yall) a maplist/3 and append/3.

Counting elements of a list in a predicate

I have something like this:
plane(1, 2, aaa, b([1,2,3,4]), 3).
I'm able to access the elements of plane and show it just like above, but the problem is b([1,2,3,4]).
How can I access it to count the number of elements that is inside of that list?
If the format of these planes are always the same, then you can just pattern match (unify) that to bind the list in b to a variable, and then check the length (usage count_in_plane(+,-), i.e., provide P, get L):
count_in_plane(P, L) :-
P = plane(_,_,_, b(List), _),
length(List, L).
Let's say you have unified the parameter number 4 with variable B_list. If you would like to take the list from inside it, use unification operator =, like this:
/* Let's pretend that you do not need other parameters */
plane(_, _, _, B_list, _) :-
/* This assigns the content of the list inside b(...) to List */
B_list = b(List),
length(List, N),
write(N),
nl.
This will print 4.
Simply, like:
count( plane(_,_,_, b(List), _), R ) :- length(List, R).

Intersection of two lists of variables

How to define in ISO Prolog a (meta-logical) predicate for the intersection of two lists of variables that runs in linear time? The variables may appear in any determined order. No implementation dependent property like the "age" of variables must influence the outcome.
In analogy to library(ordsets), let's call the relation varset_intersection(As, Bs, As_cap_Bs).
?- varset_intersection([A,B], [C,D], []).
true.
?-varset_intersection([A,B], [B,A], []).
false.
?- varset_intersection([A,B,C], [C,A,D], Inter).
Inter = [A,C].
or
Inter = [C,A].
?- varset_intersection([A,B],[A,B],[A,C]).
B = C
or
A = B, A = C
?- varset_intersection([A,B,C],[A,B],[A,C]).
idem
That is, the third argument is an output argument, that unifies with the intersection of the first two arguments.
See this list of the built-ins from the current ISO standard (ISO/IEC 13211-1:1995 including Cor.2).
(Note, that I did answer this question in the course of another one several years ago. However, it remains hidden and invisible to Google.)
If term_variables/2 works in a time linear with the size of its first argument, then this might work:
varset_intersection(As, Bs, As_cap_Bs):-
term_variables([As, Bs], As_and_Bs),
term_variables(As, SetAs),
append(SetAs, OnlyBs, As_and_Bs),
term_variables([OnlyBs, Bs], SetBs),
append(OnlyBs, As_cap_Bs, SetBs).
Each common variable appears only once in the result list no matter how many times it appears in the two given lists.
?- varset_intersection2([A,_C,A,A,A], [A,_B,A,A,A], L).
L = [A].
Also, it might give strange results as in this case:
?- varset_intersection([A,_X,B,C], [B,C,_Y,A], [C, A, B]).
A = B, B = C.
(permutation/2 might help here).
If copy_term/2 uses linear time, I believe the following works:
varset_intersection(As, Bs, Cs) :-
copy_term(As-Bs, CopyAs-CopyBs),
ground_list(CopyAs),
select_grounded(CopyBs, Bs, Cs).
ground_list([]).
ground_list([a|Xs]) :-
ground_list(Xs).
select_grounded([], [], []).
select_grounded([X|Xs], [_|Bs], Cs) :-
var(X),
!,
select_grounded(Xs, Bs, Cs).
select_grounded([_|Xs], [B|Bs], [B|Cs]) :-
select_grounded(Xs, Bs, Cs).
The idea is to copy both lists in one call to copy_term/2 to preserve shared variables between them, then ground the variables of the first copy, then pick out the original variables of the second list corresponding to the grounded positions of the second copy.
If unify_with_occurs_check(Var, ListOfVars) fails or succeeds in constant time, this implementation should yield answers in linear time:
filter_vars([], _, Acc, Acc).
filter_vars([A|As], Bs, Acc, As_cap_Bs):-
(
\+ unify_with_occurs_check(A, Bs)
->
filter_vars(As, Bs, [A|Acc], As_cap_Bs)
;
filter_vars(As, Bs, Acc, As_cap_Bs)
).
varset_intersection(As, Bs, As_cap_Bs):-
filter_vars(As, Bs, [], Inter),
permutation(Inter, As_cap_Bs).
This implementation has problems when given lists contain duplicates:
?- varset_intersection1([A,A,A,A,B], [B,A], L).
L = [B, A, A, A, A] ;
?- varset_intersection1([B,A], [A,A,A,A,B], L).
L = [A, B] ;
Edited : changed bagof/3 to a manually written filter thanks to observation by #false in comments below.
A possible solution is to use a Bloom filter. In case of collision, the result might be wrong, but functions with better collision resistance exist. Here is an implementation that uses a single hash function.
sum_codes([], _, Sum, Sum).
sum_codes([Head|Tail], K, Acc,Sum):-
Acc1 is Head * (256 ** K) + Acc,
K1 is (K + 1) mod 4,
sum_codes(Tail, K1, Acc1, Sum).
hash_func(Var, HashValue):-
with_output_to(atom(A), write(Var)),
atom_codes(A, Codes),
sum_codes(Codes, 0, 0, Sum),
HashValue is Sum mod 1024.
add_to_bitarray(Var, BAIn, BAOut):-
hash_func(Var, HashValue),
BAOut is BAIn \/ (1 << HashValue).
bitarray_contains(BA, Var):-
hash_func(Var, HashValue),
R is BA /\ (1 << HashValue),
R > 0.
varset_intersection(As, Bs, As_cap_Bs):-
foldl(add_to_bitarray, As, 0, BA),
include(bitarray_contains(BA), Bs, As_cap_Bs).
I know that foldl/4 and include/3 are not ISO, but their implementation is easy.

Split list on given element

I have a list C and I want to split the list using the element c in the list.
The expected results are as example:
?- split([a,c,a,a,c,a,a,a],X).
X = [[a],[a,a],[a,a,a]].
Can anybody help? Thanks in advance.
I can remove the c in the list now and here is my codes.
split([],[]).
split([H|T],[H|S]) :- H=a,split(T,S).
split([H|T],S) :- H=c,split(T,S).
Your "remove c" predicate would look better like this:
remove_c([c|T], S) :-
remove_c(T, S).
remove_c([a|T], [a|S]) :-
remove_c(T, S).
This still only works for lists that have only c and a in them.
If you want to "split", this means you at least need another argument, to collect the a's between the c's. For example:
split_on_c(List, Split) :-
split_on_c_1(List, Split, []).
split_on_c_1([], [Acc], Acc).
split_on_c_1([c|Rest], [Acc|Split], Acc) :-
split_on_c_1(Rest, Split, []).
split_on_c_1([a|Rest], Split, Acc) :-
split_on_c_1(Rest, Split, [a|Acc]).
Again, this expects lists of a and c only. It could also be done in different ways, but this is a start.
While learning a language you need to get accomplished to common abstractions already established (in simpler terms, use libraries). What about
split(In, Sep, [Left|Rest]) :-
append(Left, [Sep|Right], In), !, split(Right, Sep, Rest).
split(In, _Sep, [In]).
to be used like
?- split([a,c,a,a,c,a,a,a],c,R).
R = [[a], [a, a], [a, a, a]].
Use the meta-predicate splitlistIf/3 together with reified term equality
(=)/3, like this:
Here is the query the OP gave in the question:
?- splitlistIf(=(c),[a,c,a,a,c,a,a,a],Xs).
Xs = [[a],[a,a],[a,a,a]].
Note that above code is monotone, so the following query gives reasonable results:
?- splitlistIf(=(X),[Y,X,Y,Y,X,Y,Y,Y],Xs), Y = a, X = c.
X = c,
Y = a,
Xs = [[a],[a, a],[a, a, a]].

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