Related
I have a list, for example [(1,2), (3,4), (5,2), (4,2), (8,0)], and I want to remove, for example, all elements that are not (_,2). So in this case, I'd end up with a list like this: [(1,2), (5,2), (4,2)].
I was trying:
conta_pos(NL, _, NL):-
Idk what to do here, !.
conta_pos([(L,C)|T], C, _):-
conta_pos_aux([()], C, _).
conta_pos([(_,C)|T], _, _):-
conta_pos(T, _, _).
The first argument represents the initial list, the second is the element I want the list to keep, and the third would be my new list.
Keep in mind that I'm very new to Prolog, please.
(The actual thing I want to do is to count the number of elements in the initial that are, in this example (_, 2), so that would 3. I was thinking of then using length\2 to count them, but if you have a better suggestion, I'm all open to it! And if you want to know what the whole thing I have to do is, feel free to ask)
You're describing lists, so you could use DCGs for the task, since they usually yield easily readable code. Furthermore, you could use an additional argument to count the elements in the list as it's being traversed. Consider the following code:
list_filtered_length(L,F,Len) :- % the filtered list F is described
phrase(filtered_len(L,Len,0),F). % by the DCG filtered_len//3
filtered_len([],N,N) --> % if the list is empty, the counter is the length
[]. % and the filtered list is empty
filtered_len([(A,2)|Ps],N,C0) --> % if the head of the list is (A,2)
{C1 is C0+1}, % the counter is increased
[(A,2)], % (A,2) is in the filtered list
filtered_len(Ps,N,C1). % the same for the tail
filtered_len([(_,B)|Ps],N,C) --> % if the head of the list is (_,B)
{dif(B,2)}, % with B not being 2, it's not in the list
filtered_len(Ps,N,C). % the same for the tail
Querying this predicate with your example yields the desired result:
?- list_filtered_length([(1,2),(3,4),(5,2),(4,2),(8,0)],F,Len).
F = [ (1, 2), (5, 2), (4, 2)],
Len = 3 ;
false.
Obviously, if you want to apply a different filter, you have to rewrite the two recursive DCG rules. It would be nicer to have the filter defined as a separate predicate and to pass it as an argument, therefore making the predicate more versatile. It would also be nice to have the predicate succeed deterministically if there's only a single solution. This can be realized with if_/3 and (=)/3. In order to be used as the first argument of if_/3, the filter predicate needs to reify its truth value as an additional argument:
filter_t((_,X),T) :-
if_(X=2,T=true,T=false).
As you can see, the last argument is true if the filter condition holds and false otherwise:
?- filter_t((1,1),T).
T = false.
?- filter_t((1,2),T).
T = true.
Now the predicate can be redefined with an additional argument for the filter like so:
list_filtered_by_length(L,LF,F_2,Len) :- % F_2 is the filter argument
phrase(filtered_by_len(L,F_2,Len,0),LF).
filtered_by_len([],_F_2,N,N) -->
[].
filtered_by_len([P|Ps],F_2,N,C0) -->
{if_(call(F_2,P),(X=[P], C1 is C0+1),
(X=[], C1 = C0))},
X, % X is in the filtered list
filtered_by_len(Ps,F_2,N,C1).
If the head of the list meets the filter condition (call(F_2,P)), it is in the filtered list (X=[P]) and the counter is increased (C1 is C0+1), otherwise it is not in the list (X=[]) and the counter is not increased (C1 = C0).
Now the example query succeeds deterministically:
?- list_filtered_by_length([(1,2),(3,4),(5,2),(4,2),(8,0)],F,filter_t,Len).
F = [ (1, 2), (5, 2), (4, 2)],
Len = 3.
If you want to filter for something else, just define a different filter predicate. For example, if you want to filter all pairs of equal elements from a list of pairs, you can define...
filter2_t(X-Y,T) :-
if_(X=Y,T=true,T=false).
... and then query:
?- list_filtered_by_length([a-a,b-c,d-d,e-f],F,filter2_t,Len).
F = [a-a, d-d],
Len = 2.
EDIT
Alternatively, you can express this relation quite compactly by using tfilter/3, as suggested by #false in the comments. Just as with the DCG version you pass a reifying filter predicate as third argument that is then used as the first argument of tfilter/3. Subsequently the length of the filtered list is described by the built-in length/2.
list_filtered_by_length(L,FL,F_2,Len) :-
tfilter(F_2,L,FL),
length(FL,Len).
The above queries yield the same answers as with the DCG version.
In general, suppose that you have a predicate some_condition(...other_args..., X) that succeeds if you want to keep X and fails otherwise. A simple recursion to do the filter would be:
filter([], _, []).
filter([X|Xs], Condition, Ys) :-
( call(Condition, X)
-> Ys = [X|Zs]
; Ys = Zs
),
filter(Xs, Condition, Zs).
You can then call this as:
filter(RawList, some_condition(...other args...), FilteredList).
In this specific case, the condition you want to check is whether the argument matches (_,2). The simplistic condition would be:
second_arg_2((_,2)). % Succeeds if second argument is a 2
You can make the condition as sophisticated as you wish to handle more cases.
Then you can call:
| ?- filter([(1,2), (3,4), (5,2), (4,2), (8,0)], second_arg_2, R).
R = [(1,2),(5,2),(4,2)]
yes
You can then, of course, count the results if you want using length/2.
For the count of (_,2) you can do
conta_pos(In, (_,V), Out) :-
aggregate(count, X^member((X,V), In), Out).
Result :
?- conta_pos( [(1,2), (3,4), (5,2), (4,2), (8,0)], (_,2), Out).
Out = 3.
Filtering elements matching (via unification) with a template, we have two recursive rules (one for when the element matches Z, and one for when the element does not match):
filter([X|Xs],Z,[X|Ys]) :- % X is part of the output
copy_term(Z,E), % copy free variables (e.g. the _ in (_,2))
E=X, % matches with template Z
filter(Xs,Z,Ys). % recurse
filter([X|Xs],Z,Ys) :- % X is not part of the output
copy_term(Z,E), % copy free variables
E\=X, % does NOT match with template Z
filter(Xs,Z,Ys). % recurse
filter([],_,[]). % base step
We have to use copy_term/2 to generate a new copy of free variables, otherwise it will ground the matching variables at the first step and try to match the grounded template against the remaining elements, and would fail.
?- filter([(1, 2), (3, 4), (5, 2), (4, 2), (8, 0)], (_, 2), Y), length(Y, Count).
Y = [(1, 2), (5, 2), (4, 2)],
Count = 3
Alternatively, you can directly count the matching elements with a recursive predicate, which works exactly the same as the previous solution:
count([X|Xs],Z,Count) :-
copy_term(Z,E),
E=X,
count(Xs,Z,Count1),
Count is Count1 + 1.
count([X|Xs],Z,Count) :-
copy_term(Z,E),
E\=X,
count(Xs,Z,Count).
count([],_,0).
Test:
?- count([(1,2), (3,4), (5,2), (4,2), (8,0)], (_,2), Count).
Count = 3
The two recursive rules can be merged into one, with the conditional (->) operator:
count([X|Xs],Z,Count) :-
copy_term(Z,E),
count(Xs,Z,Count1),
( E=X -> Count is Count1 + 1 ; Count=Count1 ).
count([],_,0).
I'm working on creating a board used for the Bert Bos puzzle, and I'm trying to represent the board as a list of lists.
I need to create a list of empty lists ex [ [], [] , [] , [] ] but the problem is I need the exact number of empty lists provided from the input. So for example if I give create_board(4,X), it should return X= [ [], [], [], [] ].
Here is what I have so far
generate_board(0, [[]]) :- !
generate_board(N, [[] | T]) :-
N =< 12, N >= 1,
N is N-1.
generate_board(N, T).
An easy way to create a list of a given length consisting of the same element, or just empty lists in this case, is to use maplist2:
generate_board(Length, Board) :-
length(Board, Length),
maplist(=([]), Board).
Here, maplist(=([]), Board) will call =([], Element) (the canonical form of [] = Element) for each Element in Board, thus unifying each element with []:
| ?- generate_board(4, L).
L = [[],[],[],[]]
yes
| ?-
You can extend this concept to do a two-dimensional empty board. Think of the board as a list of rows (with length Length) and each row as a list of elements (with length Width):
generate_board(Length, Width, Board) :-
length(Row, Width),
maplist(=([]), Row), % A row of empty lists, forming an empty row
length(Board, Length),
maplist(=(Row), Board). % A list of empty rows
| ?- generate_board(4,3, L).
L = [[[],[],[]],[[],[],[]],[[],[],[]],[[],[],[]]]
yes
| ?-
Here is just the reason why your program did not work (apart from the . in place of ,). Because this fragment fails, also your original program fails. You have to generalize the visible part somehow.
:- op(950,fy,*).
*_.
generate_board(0, [[]]) :- !
generate_board(N, _/*[[] | T]*/) :- % 2nd
* N =< 12, % 2nd
* N >= 1, % 2nd
N is N-1,
* generate_board(N, T). % 1st generalization
?- generate_board(4, B).
This method works for pure, monotonic Prolog programs. You have, however, used a cut which restricts generalization. In this case, one really has to pay attention not to generalize anything prior to the cut. The first generalization is thus the recursive goal. It is the very last goal in the clause. Only then, the other generalizations may take place
In your program without the cut, we could have generalized your program even further:
generate_board(0, _/*[[]]*/).
...
A simple solution:
generate_board(N, Board) :-
findall([], between(1, N, _), Board).
Apart from a couple of syntax errors, the main problem with your code is the line N is N-1. In Prolog, you cannot 're-assign' a variable. A variable has a single value throughout a predicate. 'N is N-1` can only succeed for a value which is equal to itself minus 1, which will obviously never be the case.
Fixing it is simple: just use a different variable for the reduced value:
generate_board(0, [[]]) :- !.
generate_board(N, [[] | T]) :-
N =< 12, N >= 1,
N2 is N-1,
generate_board(N2, T).
?- generate_board(4, X).
X = [[], [], [], [], []]
This gives a result, but it's one more element than intended. Can you figure out how to fix this yourself (hint: look at what the base case returns for input 0)
Here's a code which recursively add the element X at the end of the list.
app(X, [], [X]).
app(X, [Y | S], [Y | S2]) :- app(X, S, S2).
Could anyone explain me how it works? Where's the return statement, what exactly the app(X, S, S2) [Y | S], [Y | S2] do?
You don't need return statement everything is done by unification (simply pattern matching). The clause:
app(X, [Y | S], [Y | S2])
states that the second argument is a list with head Y and tail S and the third argument is a list with head Y and tail S2. So it forces (by using unification) the heads of the two lists to be the same. Recursively the two lists become identical except the fact that the third argument list has one more element in the end (element X) and this is defined by the first clause. Note that second clause only works for lists with one or more elements. So as a base of the recursion when we examine the empty list (in the second parameter) then the third list due to first clause contains only one more element the element X.
Prolog programs are made by defining facts and rules. You define facts and rules, and Prolog interpreter tries to come up with solutions to make them true. Other than this basic concept, you need to know two other important concepts which Prolog programmers use extensively.
These are:
Input and Output parameters: There are no return statements in Prolog. Some variables will be results (outputs) and some others will be the inputs. In your program, the first and second parameters are input and the last one is the output.
Pattern Matching: If a list is expressed as [Head|Tail]. Head is the first element and Tail is a list of the remaining elements.
When you call app, for example, as app(5, [1, 2, 3, 4], L)., Prolog interpreter tries to come up with values for L such that app is true.
Prolog interpreter is solving the problem in the following steps:
In order to make app(X, [Y | S], [Y | S2]) true, the first element of the last parameter need to become Y. So, in my example, L becomes [1, S2].
Then it tries to match the rule app(X, S, S2). Here S is [2, 3, 4] and S2 is the output parameter for the next run. Then Step 1 gets repeated but with app(5, [2, 3, 4], S2) and after that S2 becomes [2, S2]. So, L, now, becomes [1, 2, S2].
This same thing gets repeated (recursion) and L is populated as [1, 2, 3, 4, S2].
Now, the second parameter is empty. So, the first fact app(X, [], [X]) is matched. In order to make this true, the last parameter becomes a list containing just X (which is 5 in this case), which results in L being [1, 2, 3, 4, 5].
I want to create a list consisting of N elements.
I write the following code:
DOMAINS
list = integer*
PREDICATES
create(integer, integer, list)
CLAUSES
create(_, 0, []).
create(Start, End, [Start|T]):-
Start < End + 1,!,
Counter = Start + 1,
create(Counter, End, T).
GOAL
create(1, 5, L).
But it returns me No Solution.
On the other hand if I change the direction of my Counter like this:
DOMAINS
list = integer*
PREDICATES
create(integer,list)
CLAUSES
create(0,[]).
create(N,[N|T]):-
N > 0,
NN = N - 1,
create(NN,T).
GOAL
create(5,L).
It returns me 1 Solution: L=[5,4,3,2,1]. It's working good, but not in the order.
What wrong in my first variant of code?
You need to make some adjustments to your program:
The stop clause is never unified, because you don't decrement the
End term.
Counter need to be evaluated to the expression Start + 1, so use the is/2 operator.
You don't need the cut on the second clause, but on the first one.
Program:
create(X, X, [X]):- !.
create(Start, End, [Start|T]):-
Start =\= End,
Counter is Start + 1,
create(Counter, End, T).
Consult(You need the list to be instantiated, so use a variable instead of the empty list)
?- create(1,5, L).
L = [1, 2, 3, 4, 5].
In the first variant the base case is wrong:
create(_, 0, []).
Here the End argument is 0, but your non-base rule is never modifying the End, it is running on Start.
So your base case should match whenever Start is equal to the End:
create(E, E, []).
Basically, I need to write a predicate, even_elts(L,M), such that L is a new list generated that contains only the even indexed elements from M (0th, 2nd, 4th, etc)
add_tail([X],[],X).
add_tail([H|NewT],[H|T],X) :-
add_tail(NewT,T,X).
even_elts(L,[]) :- L = [].
even_elts(L,M) :- even_elts2(L,M,1).
even_elts2(L,[H2|T2],Ct) :-
Ct2 is Ct + 1,
((Ct2 mod 2) =:= 0, add_tail(L,L2,H2), even_elts2(L2,T2,Ct2); even_elts2(L,T2,Ct2)).
even_elts2(_,[],_) :- !.
This works if M is empty or contains 1 or 2 elements. But, it only gets the first even indexed element from M, not the rest. Any pointers
EDIT: Solved the problem a different way, by removing the odd indexed elements rather than trying to create a new list and copying the data over. But, if someone can figure out a solution for my original code, I would be interested to see.
You're making this much more complicated than it is. You can use pattern matching to get each even element out, then collect those in the second (output) argument.
% an empty list does not have even elements
even_elts([], []).
% for all other lists, skip the second element (_),
% add the first to the output, recurse
even_elts([X, _ | L], [X | R]) :-
even_elts(L, R).
Just another approach with accumulator:
even_elts(L,M) :-
even_elts(M,0,[],L).
even_elts([H|T],I,Acc,Ans) :-
( I mod 2 =:= 0, append(Acc,[H], AccNew)
; I mod 2 =:= 1, AccNew = Acc
),
Inew is I + 1,
even_elts(T,Inew,AccNew,Ans).
even_elts([],_,Acc,Acc).
And
?- even_elts(X,[1,2,3,4,5]).
X = [1, 3, 5] ;
evens([A,B|C], [A|D]):- !, .....
evens(X, X).
is all you need. Fill in the blanks. :)