Counting elements of a list in a predicate - prolog

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

Related

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.

Prolog: Replace list items using facts

im trying to make a function that loops through a list and replaces the element if it matches a fact.
I was able to implement a simple replacement that replaces every element in the list.
replace([X|T], Y, [Y|T2]) :- replace(T,Y,T2).
replace([],X,[X]).
so this just replaces every list item in X with Y.
Now i want to replace every list item in X using a fact like so:
replace([1,2,3], [ rule(1, [one]), rule(2, [two]) ], Result)
so if the list is [1,2,3], the result will be [one, two, 3]
how would I do this ?
I do prefer to use higher order library support
replace(In, Replacements, Out) :-
maplist(replace_one(Replacements), In, Out).
replace_one(RepList, Rep, Val) :-
memberchk(rule(Rep, [Val]), RepList) -> true ; Rep = Val.
I think it can be simply:
replace([], _, []).
replace([H|T], Rules, [R|TR]) :-
( memberchk(rule(H, [R]), Rules)
-> true
; H = R
),
replace(T, Rules, TR).
You can do it by adding a second rule that goes through the list of replacements, and either picks the first one that matches, or leaves the item unchanged, like this:
replace([],_,[]).
replace([H|T], L, [RH|RT]) :- replace(T,L,RT), replace_one(H, L, RH).
replace_one(H, [], H).
replace_one(H, [rule(H,B)|_], B).
replace_one(H, [rule(A,_)|T], R) :- H \= A, replace_one(H, T, R).
Demo on ideone.

adding element into the List with prolog

I got a problem with this
I want to make a list of target position like if I type
?- extractIndices([5,6,7,8,9,5,6],6,List).
it should return
List = [1,6]
which gives all position of 6 in that list.
I wrote code like this:
extractIndices(List , Item, [Index | Indecis]) :-
indexOf(List , Item, Index).
indexOf([Item | _], Item, 0).
indexOf([_ |Tail], Item, Index):-
indexOf(Tail, Item, Index1),
Index is Index1+1.
and this gives me
?- extractIndices([5,6,7,8,9,5,6],6,L).
L = [1|_G2870] ;
L = [6|_G2870] ;
false.
It will be so thankful if someone can help me fix this...
Thank you.
You have provided two rules for indexOf, one which handles the head of the list, ignoring the tail, and one which handles the tail, ignoring the head. This results in two different solutions to your query as shown.
The predicate nth0 can be used to map positions to items in a list.
The easiest way to use it is going to be with findall:
extractIndices(List , Item, Indices) :-
findall(N, nth0(N, List, Item), Indices).
You can also make your own solution using something like indexOf. But you probably want to provide two different rules: one for the base case (usually an empty list), and one recursive case which solves it for the head, and then calls indexOf again on the tail.
I would use the same code as Edmund (i.e. findall + nth0), but for learning purpose a correction to your code it's worth to show:
extractIndices(List , Item, Indices) :-
indexOf(List, Item, 0, Indices).
indexOf([X|Items], Item, I, Is) :-
( X == Item -> Is = [I|Rs] ; Is = Rs ),
J is I + 1,
indexOf(Items, Item, J, Rs).
indexOf([], _, _, []).
test:
?- extractIndices([5,6,7,8,9,5,6],6,L).
L = [1, 6].

Prolog Subtract List Unification

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.

Pattern matching list of lists

I have a problem where I have a list like this:
[[el1, el2, el3],
[el4, el5, el6],
[[el7, el8, el9], [el10, el11, el12], ..... , [elxx, elyy, elzz]],
[el, el, el]...]]
I want to pattern match the inner list of lists, the
[el7, el8, el9], [el10, el11, el12], ..... , [elxx, elyy, elzz]
How can this be done?
As of now I patternmatch the other elements with
my_method([[El1, El2, El3] | Rest]).
UPDATE
I want to pattern match if the next item of the list is a list of lists - I will be iterating over this list, removing item after item. There can be any number of lists of lists, and they can contain any number of items. They can also contain lists of lists. In fact, I will recursively call the same processing method whenever I come upon a list of lists.
All bottom level lists will have three elements, however these elements might be different:
[1, p, neg(5,6)]
[5, neg(7,6), assumption]
You said "I will be iterating over this list, removing item after item", so here's code that does just that, assuming an "item" is a three-element list of non-lists.
nested_member(X,X) :-
X = [A,_,_],
\+ is_list(A).
nested_member(X,[L|_]) :-
nested_member(X,L).
nested_member(X,[_|L]) :-
nested_member(X,L).
This can be used to backtrack over the "items":
?- nested_member(X,[[el1, el2, el3], [el4, el5, el6],
[[el7, el8, el9], [el10, el11, el12],[elxx, elyy, elzz]]]).
X = [el1, el2, el3] ;
X = [el4, el5, el6] ;
X = [el7, el8, el9] ;
X = [el10, el11, el12] ;
X = [elxx, elyy, elzz] ;
false.
I you want, you can even find out how deep in the list the items were found:
nested_member(X,L,D) :-
nested_member(X,L,0,D).
nested_member(X,X,D,D) :-
X = [A,_,_],
\+ is_list(A).
nested_member(X,[L|_],D0,D) :-
D1 is D0+1,
nested_member(X,L,D1,D).
nested_member(X,[_|L],D0,D) :-
nested_member(X,L,D0,D).
You can use predicates similar to the following.
qualify([], []).
qualify([H|T], [HN|TN]) :- qualify_one(H, HN), qualify(T, TN).
qualify_one([H|_], N) :- qualify_one(H, N1), N is N1 + 1, !.
qualify_one(_, 0).
What qualify does is for each member of the list to find out on what level of the scale “not a list”, “simple list”, “list of lists”, … it is, based on the first item.
Example:
?- qualify([1,[2,3,3],[[4,5,6], [7,8,9]]], NS).
NS = [0, 1, 2].

Resources