I have a question in a course assignment.
Consider repetitive lists of the form [a,a,a,b,b,c,a,a,a,a] and their compact form, defined as lists of couples, [[a,3],[b,2],[c,1],[a,4]].
Define the predicate compress/2 such that compress(+L1, ?L2) is satisfied if, given a list L1, L2 is its compact form.
So far, I have come up with the code below:
compress(X,[[X,1]]).
compress([H1,H2|T1],[[H1,C]|T2]):-
H1 = H2,
compress(T1,T2),
C is R + 1.
I am not sure if I am doing it right. Could someone please point to the right direction.
Here are some ideas to get you started.
You're going to need to keep a running count of repeated elements since your results have counters. So right off, consider an auxiliary predicate that includes the counter, which is a typical way of handling it in Prolog. This use of a counter is commonly referred to as an accumulator.
compress(L, C) :-
compress(L, 1, C). % Start counter at 1
Now you'll need to consider a few different cases:
compress([], _, []). % This is an easy one!
This says that if I compress an empty list, I get an empty list.
compress([H], Count, [[H,Count]]). % This is an easy one!
This one says if I compress a list of one element and my current running count is Count, then the result is [[H, Count]].
compress([H, H|T], Count, TC) :-
...
This is the case where I have a running count and the element is still repeating. The result is going to be a list TC but I don't know what it looks like yet since we're still in a repeating cycle and it will need to be determined through recursion. What should this predicate look like? In your example, you included a count in the result when the first two elements were the same, which is not the right time to include the count (see the clause below).
compress([H1, H2|T], Count, [[H1,Count]|TC]) :-
dif(H1, H2),
...
This is the case where I have a running count and the repeating stops at H1. Since the repeating of the current cycle ends with H1, we know the result looks like [[H1, Count]|TC] because H1 has repeated Count times. We just have yet to determine the rest of the list TC through recursion. What should this predicate implementation look like?
There are other ways of doing the above logic (e.g., with -> and ; construct, etc), but this will keep it simple.
Try to think of these as rules where the head of the predicate clause is the assertion which will be true if the following elements of the clause are true. And think recursively.
As an afterthought, this could be done without a separate accumulator by using the result to carry the accumulator:
compress([], []).
compress([H], [[H,1]]).
compress([H1,H2|T], [[H1,1]|R]) :-
dif(H1, H2),
compress(...). % left as an exercise
compress([H,H|T], [[H,N]|R]) :-
N #= N1 + 1,
compress(...). % left as an exercise
I chose to do it this way:
?- compress([a,a,a,b,b,c,a,a,a,a],L), write(L), nl, fail.
compress(X,R) :-
enumerate(X,Y),
collapse(Y,R).
enumerate([],[]).
enumerate([H|T],[[H,1]|R]) :- enumerate(T,R).
collapse([],[]).
collapse([X],[X]).
collapse([[X,N1],[X,N2]|T],R) :- N is N1 + N2, collapse([[X,N]|T],R).
collapse([[X,N1],[Y,N2]|T],[[X,N1]|R]) :- X \= Y, collapse([[Y,N2]|T],R).
The enumerate predicate simply maps [a, a, a, b, b, c, a, a, a, a] to [[a, 1], [a, 1], [a, 1], [b, 1], [b, 1], [c, 1], [a, 1], [a, 1], [a, 1], [a, 1]].
Then I collapse this list down by matching the first two heads of the list - if they unify add the values and try to collapse again. If they don't unify then pop one element off of the list and collapse again. Otherwise there are two base cases - an empty list and a list with one element.
The result is: [[a, 3], [b, 2], [c, 1], [a, 4]].
Here's how you could do it using splitlistIfAdj/3
in combination with dif/3.
First, determine the runs of equal adjacent list items:
?- splitlistIfAdj(dif, [a,a,a,b,b,c,a,a,a,a], Xss).
Xss = [[a,a,a],[b,b],[c],[a,a,a,a]].
Then, map each run to its length using maplist/3 and length/2:
?- maplist(length, [[a,a,a],[b,b],[c],[a,a,a,a]], Ls).
Ls = [3,2,1,4].
Almost done! Let's put it all together using Prolog lambdas:
:- use_module(library(lambda)).
list_compressed(Xs, Yss) :-
splitlistIfAdj(dif, Xs, Xss),
maplist(\Es^(E-N)^(Es=[E|_],length(Es,N)), Xss, Yss).
Sample query:
?- list_compressed([a,a,a,b,b,c,a,a,a,a], Xss).
Xss = [a-3,b-2,c-1,a-4].
Related
I'm having trouble figuring out how to find the sum of the integers that are in a list of pairs like so:
[[a, 1], [b, 2], [c, 3], [d, 4]]
I tried something like this, since it is reminiscent of a regular sum function:
sum([], 0).
sum([[_,Head]|[_,Tail]], Sum) :-
sum([_,Tail], Sum2),
Sum is Head+Sum2.
With the call being:
sum([[a, 1], [b, 2], [c, 3], [d, 4]], Total),
write('Sum = '), write(Total).
But that doesn't work. It prints out false, when it should print out the sum, which would be 10 here.
In your attempt to define the predicate sum/2, you're not handling the lists of lists correctly. Try:
sum(Lists, Sum) :-
sum(Lists, 0, Sum).
sum([], Sum, Sum).
sum([[_,N]| Lists], Sum0, Sum) :-
Sum1 is Sum0 + N,
sum(Lists, Sum1, Sum).
This version uses an accumulator to enable a tail-recursive definition. Sample call:
| ?- sum([[a, 1], [b, 2], [c, 3], [d, 4]], Sum).
Sum = 10
yes
I think it might help to split this into two tasks:
create a new list of the second item of each sublist; and
sum up that list.
This makes it easier to tackle the two problems, and furthermore you now have two extra predicates that can be used for other purposes.
We can obtain a list of the second item of the sublists with:
item2list([], []).
item2list([[_, X|_]|T], [X|T2]) :-
item2list(T, T2).
or we can use maplist/3 [swi-doc] and nth1/3 [swi-doc]:
item2list(L1, L2) :-
maplist(nth1(2), L1, L2).
or we can write item2list in terms of findall/3 [swi-doc] and member/2 [swi-doc]:
item2list(L1, L2) :-
findall(X, member([_,X|_], L1), L2).
although here the predicate is not bidirectional.
For example:
?- item2list([[a, 1], [b, 2], [c, 3], [d, 4]], L).
L = [1, 2, 3, 4].
I leave summing up that list as an exercise.
Whenever a goal fails that you expect to succeed, see this as an opportunity to learn (short form for logic earn = earn logic). After all, this is Prolog which was meant to mean Programming in Logic. So where is the logic in your program?
For the moment your program fails, but you expected it to succeed. Where is the culprit? Let's generalize your program such that the resulting program still fails, but is much smaller. There are two easy ways to generalize a program:
remove goals (by adding a prefix *)
remove terms (replacing term by _/*term*/
We can do this pretty blindly. No need to understand your program. Just recheck that the goal still fails. Here is what I came up with on my first try:
:- op(950, fy, *).
* _G_0. % ignore the argument _G_0
sum([], _/*0*/).
sum([_/*[_,Head]*/|[_,Tail]], Sum) :-
* sum([_,Tail], Sum2),
* Sum is Head+Sum2.
?- sum([_/*[a, 1]*/, _/*[b, 2]*/, _/*[c, 3]*/, _/*[d, 4]*/], Total).
false. % gnah - still fails
One problem has to be in the remaining visible part. Too difficult to figure out? Let Prolog explain it to you by querying the most general query:
?- sum(Xs, Sum).
Xs = []
; Xs = [_A,_B,_C].
So only two lengths of lists are possible: The empty list and a list with three elements. Note that we have currently a generalized version of the predicate. So there is no guarantee that we will find solutions for both lengths. However, we can be 100% sure that for all other lengths there will be no solution.
Let's get back at the original program and ask the most general query:
?- sum(Os, Total).
Os = [], Total = 0
; false.
Oh no, there is a single solution only. And not even a single solution for sum([_|_], Total).
So let's generalize the program again but now with respect to this failing goal:
sum([], _/*0*/).
sum([_/*[_,Head]*/|[_,Tail|_/*[]*/]], Sum) :-
sum([_,Tail], Sum2),
* Sum is Head+Sum2.
?- Os = [_|_], sum(Os, Total).
false.
In this part there must be a further error. And in fact, the goal sum([_,Tail], Sum2) is the culprit: It is about a list of exactly two elements, but the rule wants at least three
For the actual fixes, see the other answers.
This method works for pure, monotonic programs such as yours.
I'm trying to write a program in Prolog that will take in three lists (all of which are the same length) and return a list of lists.
The list of lists that I am returning is a triple that contains elements from the three lists that are being passed in. The first element of the triple is from the first list passed in, the second element of the triple is from the second list, and the third element of the triple is from the third list passed in.
What I want to have happen is the list of triples that the function is returning to return every single possible combination that you could make from the three lists being passed in.
As of now I have some code that takes the first elements of the three lists and makes a triple out of them, then takes the second element of all the lists and makes a triple out of them, and so on. Here it is below.
listCombos( [], [], [], []).
listCombos( [A|AREST], [B|BREST], [C|CREST], [[A,B,C]|SOLUTION]) :-
listCombos( AREST, BREST, CREST, SOLUTION).
My strategy for getting every combo is taking the first element of the first list and the first element in the second list and then going through each elements in the third list. Once I have done that I will move on the the first element in the first list and the second element in the second list and match those up with each element in the third list. Then after I have went through the second list move onto the first list. Let me know if more clarification on this is needed.
I'm new to Prolog so I don't understand how to turn what I'm planning to do into code. I've tried a few things but haven't been successful and have gotten some error codes I don't understand so it's hard to tell if I'm going in the right direction (I can post some of my attempts if needed). If anyone has some idea of what direction I should go in or some explanation on what I need to do that would be appreciated.
Thank you very much.
Knowing a little Prolog the most obvious solution is something like this:
listCombos(Xs, Ys, Zs, Result) :-
findall([X,Y,Z],
(member(X, Xs), member(Y, Ys), member(Z, Zs)),
Result).
It's advisable to generalize the construct you're looking for, accepting a list of lists to be combined, following the schema from this answer:
combine(Ls,Rs) :- maplist(member,Rs,Ls).
listCombos(A,B,C, SOLUTION) :- findall(R,combine([A,B,C],R),SOLUTION).
We first can solve a related problem: given a list of "heads" Hs and a list of "tails" Ts, construct all lists for all heads H in Hs, and all tails T in Ts in a list. We can do this with a predicate:
merge_all([], _, []).
merge_all([H|Hs], Ts, All) :-
merge_single(Ts, H, All, D),
merge_all(Hs, Ts, D).
merge_single([], _, D, D).
merge_single([T|Ts], H, [[H|T]|Rest], D) :-
merge_single(Ts, H, Rest, D).
For example:
?- merge_all([a, b], [[1, 4], [2, 5]], R).
R = [[a, 1, 4], [a, 2, 5], [b, 1, 4], [b, 2, 5]].
Now we can use this for example to make all cross products with Cs and the "empty set", for example if Cs = [a, b, c], then:
?- merge_all([a, b, c], [[]], RC).
RC = [[a], [b], [c]].
Given we have this result, we can make the cross product of Bs with this result. For example if Bs = [1, 4], then we obtain:
?- merge_all([a, b, c], [[]], RC), merge_all([1, 4], RC, RB).
RC = [[a], [b], [c]],
RB = [[1, a], [1, b], [1, c], [4, a], [4, b], [4, c]].
With the above generating the cross product of three sets should be straightforward, I leave this as an exercise.
The approach by Daniel Lyons is good in that it allows us to easily control the order of combinations in the cross-product of a list of lists, while keeping the order of elements in the combinations the same, of course:
cross( [], [[]] ).
cross( [XS | T], R ):-
cross( T, TC),
findall( [X | Y], ( % or:
member( Y, TC), % member( X, XS)
member( X, XS) % member( Y, TC),
),
R).
It exhibits good modularity and separation of concerns: the order of presentation is independent of the order of generation and the order of selection.
So I'm very new to prolog and have to write a predicate that is satisfiable when an integer list D is the list of prefix sums of a list A.
sums(A, D)
So for example,
sums([4,11,1,-3,8], [4,15,16,13,21]) is satisfiable
I have written this predicate over a dozen different ways to no avail. This is what I currently have written.
sums([], []).
sums([A], [A]).
sums([A|[B|C]], [A|[E|F]]) :- TOTAL is A + B, E = TOTAL, sums([C], [F]).
This somewhat works, in that it will check that the first values of each list are equal, and also check that the second element in the list is correct in that it should be 15. I understand why it works incorrectly in this way, but I am having trouble coming up with how to write it differently, in the correct way.
I have since changed the code to,
sumrunner(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A], [A], _).
sumrunner([A|B], [C|D], TOTAL) :- TOTAL is TOTAL + A, TOTAL = C,sumrunner(B, D, TOTAL).
However, now it just says false for all cases except for when the two lists are empty, and when the lists both contain one element and they are both equal to each other.
You should learn more about list notation: [A|[B|C]] can be written as [A,B|C] for example. It is now clearer that C is the tail of the list, and thus, is a list itself! Therefore, when you write sums([C], [F]), you are wrapping C and F into a list, even though they are already lists, which is your problem.
If we fix this and run your predicate, we get this:
?- sums([4,11,1,-3,8],Z).
Z = [4, 15, 1, -2, 8]
It is still wrong as you can see. The main problem is that, the recursive call sums in the third rule expresses that the prefix sums of the tail of a list are the tail of the prefix sums of the list, which is wrong because those prefix sums depend on the previous element!
To solve this, you need to introduce an extra argument to maintain the sum value throughout recursive calls:
:- use_module(library(clpfd)).
prefix_sums(L, D) :-
prefix_sums(L, 0, D).
prefix_sums([], _, []).
prefix_sums([H|T], S, [S1|T2]) :-
S1 #= H + S,
prefix_sums(T, S1, T2).
Using library(clpfd), we get the behaviour we expect:
?- prefix_sums([4,11,1,-3,8],Z).
Z = [4, 15, 16, 13, 21].
But also the reverse behaviour:
?- prefix_sums(Z, [4,15,16,13,21]).
Z = [4, 11, 1, -3, 8].
And also correct behaviour with even less information:
?- prefix_sums([A,B,C],Z).
Z = [A, _7964, _7970],
B+A#=_7964,
C+_7964#=_7970.
?- prefix_sums(X,Z).
X = Z, Z = [] ;
X = Z, Z = [_7122],
_7122 in inf..sup ;
X = [_7452, _7458],
Z = [_7452, _7482],
_7458+_7452#=_7482 ;
X = [_7770, _7776, _7782],
Z = [_7770, _7806, _7812],
_7776+_7770#=_7806,
_7782+_7806#=_7812 ;
…
Your code must be simplified a lot:
sums(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A|B], [C|D], TOTAL) :- C is TOTAL + A, sumrunner(B, D, C).
?- sums([4,11,1,-3,8], [4,15,16,13,21]).
true.
?- sums([4,11,1,-3,8], [4,15,16,14,21]).
false.
The expression C is TOTAL + A both checks the requirements and update the accumulator for the recursive step.
As a Prolog newbie, I try to define a predicate filter_min/2 which takes two lists to determine if the second list is the same as the first, but with all occurrences of the minimum number removed.
Sample queries with expected results:
?- filter_min([3,2,7,8], N).
N = [3,7,8].
?- filter_min([3,2,7,8], [3,7,8]).
true.
I tried but I always get the same result: false. I don't know what the problem is. I need help!
Here is my code:
filter_min(X,Y) :-
X == [],
write("ERROR: List parameter is empty!"),
!;
min_list(X,Z),
filter(X,Y,Z).
filter([],[],0).
filter([H1|T1],[H2|T2],Z) :-
\+ number(H1),
write("ERROR: List parameter contains a non-number element"),
!;
H1 \= Z -> H2 is H1, filter(T1,T2,Z);
filter(T1,T2,Z).
There are a couple of problems with your code:
filter([],[],0). will not unify when working with any list that does not have 0 as its minimum value, which is not what you want. You want it to unify regardless of the minimum value to end your recursion.
The way you wrote filter([H1|T1],[H2|T2],Z) and its body will make it so that the two lists always have the same number of elements, when in fact the second one should have at least one less.
A correct implementation of filter/3 would be the following:
filter([],[],_).
filter([H1|T1],L2,Z):-
\+ number(H1),
write("ERROR: List parameter contains a non-number element"),
!;
H1 \= Z -> filter(T1,T2,Z), L2 = [H1|T2];
filter(T1,L2,Z).
A bounty was offered...
... for a pure solution that terminates for (certain) cases where neither the length of the first nor of the second argument is known.
Here's a candidate implementation handling integer values, built on clpfd:
:- use_module(library(clpfd)).
filter_min(Xs,Ys) :-
filter_min_picked_gt(Xs,_,false,Ys).
filter_min_picked_gt([] ,_,true ,[]).
filter_min_picked_gt([Z|Xs],M,Picked,[Z|Zs]) :-
Z #> M,
filter_min_picked_gt(Xs,M,Picked,Zs).
filter_min_picked_gt([M|Xs],M,_,Zs) :-
filter_min_picked_gt(Xs,M,true,Zs).
Some sample queries:
?- filter_min([3,2,7,8],[3,7,8]).
true ; false. % correct, but leaves choicepoint
?- filter_min([3,2,7,8],Zs).
Zs = [3,7,8] ; false. % correct, but leaves choicepoint
Now, some queries terminate even though both list lengths are unknown:
?- filter_min([2,1|_],[1|_]).
false. % terminates
?- filter_min([1,2|_],[3,2|_]).
false. % terminates
Note that the implementation doesn't always finitely fail (terminate) in cases that are logically false:
?- filter_min([1,2|_],[2,1|_]). % does _not_ terminate
For a Prolog newbie, better start with the basics. The following works when first argument is fully instantiated, and the second is an uninstantiated variable, computing the result in one pass over the input list.
% remmin( +From, -Result).
% remmin([],[]). % no min elem to remove from empty list
remmin([A|B], R):-
remmin(B, A, [A], [], R). % remove A from B to get R, keeping [A]
% in case a smaller elem will be found
remmin([C|B], A, Rev, Rem, R):-
C > A -> remmin(B, A, [C|Rev], [C|Rem], R) ;
C==A -> remmin(B, A, [C|Rev], Rem, R) ;
C < A -> remmin(B, C, [C|Rev], Rev, R).
remmin([], _, _, Rem, R) :- reverse(Rem, R).
First, we can get the minimum number using the predicate list_minnum/2:
?- list_minnum([3,2,7,8],M).
M = 2.
We can define list_minnum/2 like this:
list_minnum([E|Es],M) :-
V is E,
list_minnum0_minnum(Es,V,M).
list_minnum0_minnum([],M,M).
list_minnum0_minnum([E|Es],M0,M) :-
M1 is min(E,M0),
list_minnum0_minnum(Es,M1,M).
For the sake of completeness, here's the super-similar list_maxnum/2:
list_maxnum([E|Es],M) :-
V is E,
list_maxnum0_maxnum(Es,V,M).
list_maxnum0_maxnum([],M,M).
list_maxnum0_maxnum([E|Es],M0,M) :-
M1 is max(E,M0),
list_maxnum0_maxnum(Es,M1,M).
Next, we use meta-predicate tfilter/3 in tandem with dif/3 to exclude all occurrences of M:
?- M=2, tfilter(dif(M),[2,3,2,7,2,8,2],Xs).
Xs = [3,7,8].
Put the two steps together and define min_excluded/2:
min_excluded(Xs,Ys) :-
list_minnum(Xs,M),
tfilter(dif(M),Xs,Ys).
Let's run some queries!
?- min_excluded([3,2,7,8],Xs).
Xs = [3,7,8].
?- min_excluded([3,2,7,8,2],Xs).
Xs = [3,7,8].
I am trying to build a list function in prolog which will hopefully do the following;
split(1, 4, [1, 2, 3, 4]). [2, 3]
split(2, 4, [1, 2, 3, 4, 5]). [3]
That is it will put all the items in a list which appear in between the two value provided.
What have I tried;
split(Start, Finish, List) :- append(List, _, [Start|Xs]),
append([Finish|Xs], _, List).
I can just never seem to get it working! I am new to prolog so please be relatively kind!!
Thanks
EDIT
Ok so I have a solution and would like to know if it could be improved. The solution is below,
% Split a list at a specified index
split(List, Index, Front, Back) :-
length(Front, Index),
append(Front, Back, List).
% Get list items inbetween members
inbetween(List, From, To, Result) :-
nth1(FromI, List, From),
nth0(ToI, List, To),
split(List, FromI, _, List1),
split(List, ToI, _, List2),
subtract(List1, List2, Result).
As you can see I followed the advice in the comments and tweaked it a little. Are there any improvements to this?
Thanks, (Could it even be possible in one predicate?)
EXAMPLE
inbetween([1,2,3,4,5,6], 2, 5, Result). % [3,4]
inbetween([a,b,c,d,e,f], a, e, Result). % [b,c,d]
I think the solution you came up with is interesting and it only needs a small adjustment to make it work correctly:
% Split a list at a specified index
split(List, Index, Front, Back) :-
length(Front, Index),
append(Front, Back, List).
% Get list items inbetween members
inbetween(List, From, To, Result) :-
nth1(FromI, List, From),
split(List, FromI, _, List1),
nth0(ToI, List1, To),
split(List1, ToI, Result, _).
The split/4 predicate is unchanged from what you have. The inbetween/4 main predicate I modified a little so that first it finds everything after the From, then it uses that result and finds everything before the To yielding the final result.
| ?- inbetween([a,b,c,a,x,b,e,f], a, b, L).
L = [] ? ;
L = [b,c,a,x] ? ;
L = [x] ? ;
(1 ms) no
A shorter version, using append/3 would be:
betwixt2(List, A, B, Result) :-
append(_, [A|T], List),
append(Result, [B|_], T).
Another approach which is more recursively based and not using library calls would be:
inbetween(List, A, B, Result) :-
split_left(List, A, R),
split_right(R, B, Result).
split_left([X|T], X, T).
split_left([_|T], X, R) :- split_left(T, X, R).
split_right([X|_], X, []).
split_right([H|T], X, [H|R]) :- split_right(T, X, R).
And finally, there's an interesting, concise solution, I hadn't considered when making my comments, using a DCG which is more transparent:
betwixt(A, B, M) --> anything, [A], collect(M), [B], anything.
anything --> [].
anything --> [_], anything.
collect([]) --> [].
collect([H|T]) --> [H], collect(T).
inbetween(List, A, B, Result) :- phrase(betwixt(A, B, Result), List).
The DCG in this case nicely spells out exactly what's happening, with the same results as above. For brevity, I could also use collect(_) in place of anything in the first clause, but didn't want to waste the unused argument.
To use a nice notation credited to #false, we can use ... as a term as shown below:
betwixt(A, B, M) --> ..., [A], collect(M), [B], ... .
... --> [].
... --> [_], ... .
collect([]) --> [].
collect([H|T]) --> [H], collect(T).