Related
The task is to delete all increasing subseries from a list. For example:
deleteInc([2,4,6,5,8,12,8,3],L) should produce L=[8,3] as result
I tried to solve it, but it's not working properly and I can't figure out the correct solving. Can somebody help me out please with a correct solution? Thanks in advance :)
My Code:
deleteInc([],[]).
deleteInc([H1,H2|T],L):-
H2>=H1,
!,
deleteInc(T,L).
deleteInc([H1,H2|T],[H2|T2]):-
H2<H1,
deleteInc(T,T2).
Here's what I did, assuming it's checking three values at a time:
deleteInc([],[]).
deleteInc([H1,H2,_H3|T],L):-
H2>=H1,
!,
deleteInc(T,L).
deleteInc([H1,H2|T],[H1,H2|T2]):-
H2<H1,!,
deleteInc(T,T2).
Example:
?-deleteInc([2,4,6,5,8,12,8,3],L).
L = [8, 3]
?-deleteInc([2,4,6,5,8,12,15,21,25,7,3],L).
L = [7, 3]
?-deleteInc([2,4,6,5,8,12,15,21,25,55,77,88,65,38],L).
L = [65, 38]
Here's an approach to collect all combinations that are not increasing:
deleteInc(L,[],L).
deleteInc([H1,H2|T],L,Rest):-
H2>=H1,
!,
deleteInc(T,L,Rest).
deleteInc([H1,H2|T],[H1,H2|L],Rest):-
H2<H1,!,
deleteInc(T,L,Rest).
Example:-
?-deleteInc([2,4,6,5,8,12,15,21,25,55,77,88,65,38],L,_).
L = []
L = []
L = [6, 5]
L = [6, 5]
L = [6, 5]
L = [6, 5]
L = [6, 5]
L = [6, 5, 65, 38]
?-deleteInc([2,4,6,5,8,12,15,21,25,7,3],L,_).
L = []
L = []
L = [6, 5]
L = [6, 5]
L = [6, 5]
L = [6, 5, 25, 7]
?-deleteInc([2,4,6,5,8,12,8,3],L,_).
L = []
L = []
L = [6, 5]
L = [6, 5]
L = [6, 5, 8, 3]
I would implement it like this:
deleteInc(In, Out):-
deleteInc(In,Out,new).
deleteInc([A],[A],new).
deleteInc([_],[],incr).
deleteInc([H1,H2|T],L,_):-
H2>=H1,
!,
deleteInc([H2|T],L,incr).
deleteInc([H1,H2|T],T2,incr):-
H2<H1,
deleteInc([H2|T],T2,new).
deleteInc([H1,H2|T],[H1|T2],new):-
H2<H1,
deleteInc([H2|T],T2,new).
A third attribute indicates if your are currently in a new or increasing streak.
If the two head elements of the list are in increasing order, the third element is pretty much ignored. Please note to forward not the list T but [H2|T] since you need the element H2 for further comparison. If you are in a streak (incr) and the current two head elements are not increasing, you do the same as before: ignore. However if you just started a new streak and the current head elements are decreasing, you put the first head element in the return list. Special cases for one element. Let's check:
?- deleteInc([2,4,6,5,8,12,8,3],L).
L = [8, 3] ;
false.
Looks good.
Another approach would be to just check for 3 succeeding elements if the middle one is smaller than the first but larger than the third. If this is the case: store the element. If this is is not the case: check the list without the first head element.
Add a rule for 2 elements left. Also there needs to be a special treatment for the very first element, because this is not covered in the main rule. By using the cut you can "catch" the left over case (two increasing elements or weird input with one or no element) with the last line. Please note that the order of the rules is important for the program to work.
deleteInc([H1,H2|L], [H1|R]):-
H1 >= H2,
!,
deleteInc1([H1,H2|L], R).
deleteInc([H1,H2|L], R):-
deleteInc1([H1,H2|L], R).
deleteInc1([H1,H2,H3|L], [H2|R]):-
H1 > H2,
H2 > H3,
!,
deleteInc1([H2,H3|L], R).
deleteInc1([A,B],[B]):-
A > B,
!.
deleteInc1([_|L], R):-
!,
deleteInc1(L, R).
deleteInc1(_,[]).
Let's test it:
?- deleteInc([2,4,6,5,8,12,8,3],L).
L = [8, 3].
Works (for lists of numbers).
And yet another solution would be to count the length of the streak and to filter out all results which have more than one element. The following code starts with count 0 for the first element and uses the successor predicate s/1 for counting:
deleteInc(In, Out):-
countInc(In,0,InC),
delInc(InC,Out).
countInc([A], C, [(A,C)]).
countInc([A,B|L], C, Out):-
A =< B,
countInc([B|L], s(C), Out).
countInc([A,B|L], C, [(A,C)|Out]):-
A > B,
countInc([B|L], 0, Out).
delInc([],[]).
delInc([(E,0)|L],[E|R]):-
delInc(L,R).
delInc([(_,s(_))|L],R):-
delInc(L,R).
Output:
?- deleteInc([2,4,6,5,8,12,8,3],L)
L = [8, 3] ;
false.
As expected
So i found a way of solving it from stackoverflow and it involves this answer:
last(X,Y) :-
append(_,[X],Y).
But i can't actually understand how this actually works.
If anyone can help me it would be really helpful.Thanks.
You can use append/3 [swi-doc] in several directions. You can for example pass a list, and look how two lists can append to that list. For example:
?- append(X, Y, [1,4,2,5]).
X = [],
Y = [1, 4, 2, 5] ;
X = [1],
Y = [4, 2, 5] ;
X = [1, 4],
Y = [2, 5] ;
X = [1, 4, 2],
Y = [5] ;
X = [1, 4, 2, 5],
Y = [] ;
false.
As you can see, there are five ways to construct that. For example with X = [] and Y = [1,4,2,5], or with X = [1] and Y = [4,2,5].
We thus define the predicate last/2 as:
last(X, L) :-
append(_, [X], L).
Notice the [X] as second parameter. We here thus specify that the second list should be a singleton list (a list with exactly one element). An empty list, or a list with two or more elements will not unify with [X].
The append/3 predicate will this aim to unify the second list with candidates like we have seen in the example. But only if the second list is an singleton list, it will match, in which case X is unified with the last element.
See the definition on the SWI-Prolog website.
One of the examples is:
?- append(X, [Last], [a,b,c]).
X = [a,b],
Last = c.
It means the Last is the single element in a list.
Think of the imperative way that X appends the "Last" to the end of the list. Then, it becomes the list [a,b,c].
Therefore, to define the last, we could:
mylast(Xs,Last):-
append(_,[Last],Xs). % doesn't care about the rest of the elements except the [Last]
Is it possible to solve the following problem in Prolog?
Let A and B be lists of numbers and let N be a number. It is known that B is sorted decreasingly. Check if N can be inserted into A so that the result is B, but do not bind any variable that occurs as a tail in either A nor B.
For example
?- insertable(34, [78, 72, 11 | Z], [78, 72, 34, 11 | Z]).
true.
?- insertable(34, [78, 72, 11 | Z], L).
L = [78, 72, 34, 11 | Z].
Can anyone help me? :)
EDIT 1: This is what I came up with.
insertable(X, List1, List2):- select(X, List2, List1), sorted(List2).
sorted([]).
sorted([_]).
sorted([X, Y | Rest]) :-
X > Y,
sorted([Y | Rest]).
However, even though it works as expected when the arguments are fully instantiated, it binds variables located in tails:
?- insertable(11, [5, 3, 2], [11, 5, 3, 2]).
true .
?- insertable(11, [5, 3, 2 | X], [11, 5, 3, 2 | X] ).
X = [] .
?- insertable(11, [5, 3, 2 | X], L ).
X = [],
L = [11, 5, 3, 2] .
EDIT 2: Here's another approach that I tried.
in(X, [], [X]).
in(X, [Head | Tail1], [Head | Tail2]) :-
X =< Head,
in(X, Tail1, Tail2).
in(X, [Head | Tail], [X, Head | Tail]) :-
X > Head.
The problem is still there:
?- in(1, [3, 2], [3, 2, 1]).
true ;
false.
?- in(1, [3, 2], L).
L = [3, 2, 1] ;
false.
?- in(1, [3, 2 | X], L).
X = [],
L = [3, 2, 1] ;
ERROR: =</2: Arguments are not sufficiently instantiated
Exception: (9) in(1, _G8394089, _G8394190) ? abort
% Execution Aborted
?- in(1, [3, 2 | X], [3, 2, 1 | X]).
X = [] ;
X = [1] ;
X = [1, 1] ;
X = [1, 1, 1] ;
X = [1, 1, 1, 1] ;
X = [1, 1, 1, 1, 1] ;
X = [1, 1, 1, 1, 1, 1] ;
X = [1, 1, 1, 1, 1, 1, 1] .
The trick for this exercise are the metalogical predicates var/1 and nonvar/1 which are true if the argument is a variable or not (also have a look at ground/1, atom/1 and integer/1). To make the difference is a little clumsy because we need to keep the list L1 in the head and unify after we know it is a variable or not.
What also might have confused you is the error message of the arithmetic comparison. For that to work, both arguments must be ground. When you don't test for non-var of the tail, the unification automatically instantiates the tail with [Head|Tail1]. That always leads to a comparison number <= Head which leads to the error you had.
The following code assumes you would also like to insert into lists that have a closed tail. If not, the first rule needs to be removed.
in(X, L1, [X]) :- % terminal case for empty list (i.e. tail is not a variable)
nonvar(L1),
L1 = [].
in(X, Xs, [X | Xs]) :- % terminal case if the tail is a variable
var(Xs).
in(X, L1, [N | Zs]) :- % recursive case X =< N
nonvar(L1),
L1 = [N | Ys],
X =< N,
in(X, Ys, Zs).
in(X, L1, [X, N | Ys]) :- % recursive case X > N
nonvar(L1),
L1 = [N | Ys],
X > N.
When we test we can insert 1 in front of a variable tail (after the first result, there are still paths to test but all fail, leading to the false after continuing the query):
?- in(1,Xs,Ys).
Ys = [1|Xs] ;
false.
Also, the inserted element must be 1, so this one should fail:
?- in(1,Xs,[2|Ys]).
false.
It seems the recursion properly propagates to the end:
?- in(1,[3, 2 | Xs], Zs).
Zs = [3, 2, 1|Xs] ;
false.
Inserting in the middle also works:
?- in(2,[3,1 |Xs],Zs).
Zs = [3, 2, 1|Xs].
And finally the test case you tried to solve before:
?- in(34, [78, 72, 11 | Z], [78, 72, 34, 11 | Z]).
true ;
false.
What still doesn't work is if you have variables occurring in your list:
?- in(2,[3,A,1|Xs],Zs).
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR: [10] 2=<_8374
ERROR: [9] in(2,[_8406,1|_8414],[_8418|_8420]) at ./prolog/so-3.pl:9
ERROR: [8] in(2,[3,_8458|...],[3,_8470|_8472]) at ./prolog/so-3.pl:10
ERROR: [7] <user>
Exception: (9) in(2, [_7488, 1|_7496], _7820) ? a
An easy way out would be to guard the comparison with an integer(N) to get
?- in(2,[3,A,1|Xs],Zs).
false.
But then we should also guard against the inserted element being non-integer and the lists having decending integers followed with a variable tail. Alternatively, we could throw a better exception in these cases.
I need to write a program in prolog that accepts a list, reverses that list and the appends that to the end of the original list.
Example:
list_rList([1,2,3],X)
X = [1,2,3,3,2,1]
So far I have been able to reverse the list, but I can manage to append the reverse list to the original list.
Here is what I have:
list_rList([],[]).
list_rList([H|T],R):- list_rList(T,RevT), append(RevT,[H],R).
Here is a solution which will work correctly in all directions:
list_rList(L, T) :-
list_rList(L, [], T).
list_rList([], A, A).
list_rList([H|T], C, [H|T2]) :-
list_rList(T, [H|C], T2).
The second argument will accumulate the reversed list, and the third one will accumulate the result: each element of the original list is appended at the beginning of that third argument, and its tail is the second argument once we have emptied the first list.
Some example queries:
?- list_rList([1,2,3],Z). % What you asked
Z = [1, 2, 3, 3, 2, 1].
?- list_rList([1|T],Z). % With a variable tail
T = [],
Z = [1, 1] ;
T = [_G1659],
Z = [1, _G1659, _G1659, 1] ;
T = [_G1659, _G1668],
Z = [1, _G1659, _G1668, _G1668, _G1659, 1]
…
?- list_rList(Z,[1,2,3,3,2,1]). % The original list from the result
Z = [1, 2, 3] ;
false.
?- list_rList(Z,[1,2,3,3,2]). % Check that a list can be the result of this predicate
false.
?- list_rList(Z,[1,2,3,X,Y,3,2,1]). % With variable elements in the result
Z = [1, 2, 3, Y],
X = Y ;
false.
?- list_rList(L,Z). % With completely free arguments
L = Z, Z = [] ;
L = [_G1623],
Z = [_G1623, _G1623] ;
L = [_G1623, _G1632],
Z = [_G1623, _G1632, _G1632, _G1623] ;
L = [_G1623, _G1632, _G1641],
Z = [_G1623, _G1632, _G1641, _G1641, _G1632, _G1623] ;
L = [_G1623, _G1632, _G1641, _G1650],
Z = [_G1623, _G1632, _G1641, _G1650, _G1650, _G1641, _G1632, _G1623]
…
you could do something like this:
accRev([H|T],A,L,R) :- accRev(T,[H|A],L,R).
accRev([],A,L,R) :- append(L,A,R).
list_rList(L,R) :- accRev(L,[],L,R).
Here, first the list is reversed using an accumulator (the second argument of accRev) and once this is finished, the original list (which is kept in the third argument of accRev) is prepended.
I want to access list permutation and pass it as argument to other functions.
This is the permutation code:
takeout(X,[X|R],R).
takeout(X,[F|R],[F|S]) :-
takeout(X,R,S),
write(S).
perm([X|Y],Z) :-
perm(Y,W),
takeout(X,Z,W).
perm([],[]).
To start with, let's redefine your predicates so they don't do any unnecessary I/O:
takeout(X,[X|R],R).
takeout(X,[F |R],[F|S]) :- takeout(X,R,S).
perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).
perm([],[]).
Now you have what could be considered a "pure" permutation function:
?- perm([1,2,3], X).
X = [1, 2, 3] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [1, 3, 2] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.
So, suppose you have a max_heap function that takes a list of values and produces a tree. I'll let you worry about that, so let's just posit that it exists and is called max_heap/2 and let's further posit that you have a way to display this attractively called display_heap/1. To "take" the permutation and "send" it as a parameter to these functions, you're really saying in math-ese: suppose P is a permutation of X, let's make a max_heap with it and display it. Or, suppose P is a permutation of X, H is a max heap made from X, let's display H:
show_heaps(List) :- perm(List, P), max_heap(P, H), display_heap(H).
This says the same thing as my English sentence: suppose P is a permutation of the list, then H is a heap representation of it, then display it. Technically, display_heap/1 is still a predicate which could be true or false for a given heap. In practice, it will always be true, and if you run this you'll still have to hit ; repeatedly to say, give me another solution, unless you use a failure-driven loop or an extralogical predicate like findall/3 to cause all the solutions to be found.
Edit: Let's discuss failure-driven loops and findall/3. First let me add some new predicates, because I don't know exactly what you're doing, but it doesn't matter for our purposes.
double([X|Xs], [Y|Ys]) :- Y is X*2, double(Xs, Ys).
double([],[]).
showlist(Xs) :- print(Xs).
So now I have a predicate double/2 which doubles the values in the list and a predicate showlist/1 that prints the list on standard output. We can try it out like so:
?- perm([1,2,3], X), double(X, Y), showlist(Y).
[2,4,6]
X = [1, 2, 3],
Y = [2, 4, 6] ;
[4,2,6]
X = [2, 1, 3],
Y = [4, 2, 6] ;
[4,6,2]
X = [2, 3, 1],
Y = [4, 6, 2] ;
[2,6,4]
X = [1, 3, 2],
Y = [2, 6, 4] ;
[6,2,4]
X = [3, 1, 2],
Y = [6, 2, 4] ;
[6,4,2]
X = [3, 2, 1],
Y = [6, 4, 2] ;
false.
When you type ; you're saying, "or?" to Prolog. In other words, you're saying "what else?" You're telling Prolog, in effect, this isn't the answer I want, try and find me another answer I like better. You can formalize this process with a failure-driven loop:
?- perm([1,2,3], X), double(X, Y), showlist(Y), fail.
[2,4,6][4,2,6][4,6,2][2,6,4][6,2,4][6,4,2]
false.
So now you see the output from each permutation having gone through double/2 there, and then Prolog reported false. That's what one means by something like this:
show_all_heaps(List) :- perm(List, X), double(X, Y), showlist(Y), nl, fail.
show_all_heaps(_).
Look at how that works:
?- show_all_heaps([1,2,3]).
[2,4,6]
[4,2,6]
[4,6,2]
[2,6,4]
[6,2,4]
[6,4,2]
true.
The other option is using findall/3, which looks more like this:
?- findall(Y, (perm([1,2,3], X), double(X, Y)), Ys).
Ys = [[2, 4, 6], [4, 2, 6], [4, 6, 2], [2, 6, 4], [6, 2, 4], [6, 4, 2]].
Using this to solve your problem is probably beyond the scope of whatever homework it is you're working on though.
We can define list_permutation/2 based on same_length/2 and select/3 like this:
:- use_module(library(lists),[same_length/2,select/3]).
list_permutation(As,Bs) :-
same_length(As,Bs), % redundant goal helps termination
list_permutation_(As,Bs).
list_permutation_([],[]).
list_permutation_([A|As],Bs0) :-
select(A,Bs0,Bs),
list_permutation_(As,Bs).
Thanks to same_length/2, both of the following queries1,2 terminate universally:
?- list_permutation([1,2,3],Ys).
Ys = [1,2,3]
; Ys = [1,3,2]
; Ys = [2,1,3]
; Ys = [3,1,2]
; Ys = [2,3,1]
; Ys = [3,2,1]
; false.
?- list_permutation(Xs,[1,2,3]).
Xs = [1,2,3]
; Xs = [1,3,2]
; Xs = [2,1,3]
; Xs = [2,3,1]
; Xs = [3,1,2]
; Xs = [3,2,1]
; false.
So far, so good. But what does the answer sequence look like if there are duplicate list items?
?- list_permutation([1,1,1],Ys).
Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; false.
5/6 answers are redundant! What can we do? We simply use selectd/3 instead of select/3!
list_permuted(As,Bs) :-
same_length(As,Bs),
list_permuted_(As,Bs).
list_permuted_([],[]).
list_permuted_([A|As],Bs0) :-
selectd(A,Bs0,Bs), % use selectd/3, not select/3
list_permuted_(As,Bs).
Let's re-run above query that gave us 5 redundant solutions before!
?- list_permuted([1,1,1],Ys).
Ys = [1,1,1]
; false.
?- list_permuted(Xs,[1,1,1]).
Xs = [1,1,1]
; false.
Better! All redundant answers are gone.
Let's compare the solution set for some sample case:
?- _Xs = [1,2,1,1,2,1,1,2,1],
setof(Ys,list_permutation(_Xs,Ys),Yss),
setof(Ys,list_permuted(_Xs,Ys),Yss),
length(Yss,N).
N = 84, Yss = [[1,1,1,1,1,1,2,2,2],[1,1,1,1,1,2,1,2,2],[...|...]|...].
OK! How about empirical runtime measurements with a problem of a slightly bigger size?
We use call_time/2 for measuring the runtime in milli-seconds T_ms.
?- call_time(\+ (list_permutation([1,2,1,1,1,2,1,1,1,2,1],_),false),T_ms).
T_ms = 8110.
?- call_time(\+ (list_permuted( [1,2,1,1,1,2,1,1,1,2,1],_),false),T_ms).
T_ms = 140.
OK! And with proper compilation of if_/3 and (=)/3, list_permuted/2 is even faster!
Footnote 1: Using SICStus Prolog version 4.3.2 (x86_64-linux-glibc2.12).
Footnote 2: The answers given by the Prolog toplevel have been post-processed for the sake of readability.
If you just want to explore the permutations without the "False" in the end, this code might be helpful
takeout(X,[F |R],[F|S]) :- F\=X, takeout(X,R,S).
takeout(X,[X|R],R).
perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).
perm([],[]).
So, the output of perm([a,b],B) would be
B=[a,b]
B=[b,a]