My goal for this Prolog function is as follows:
Given two lists, x and y, return true if y can be formed from x by reversing a contiguous part of list x.
For example, if the input is x = [1, 3, 2, 4], y = [1, 2, 3, 4], the result should be "true" because we can reverse the second and third elements of x to get y.
I really have no idea, and I need some help!
Here's a straight-forward implementation using SICStus Prolog 4.3.1:
:- use_module(library(lists)).
list_singlePartReversed(Xs,Ys) :-
same_length(Xs,Ys), % Xs and Ys are lists w/the same length
dif(Xs,Ys), % Xs and Ys are not equal
append(Prefix ,Xs0 ,Xs), % Xs and Ys have common prefix
append(Prefix ,Ys0 ,Ys),
append(Part ,Suffix,Xs0), % Xs and Ys have common suffix
append(Reversed,Suffix,Ys0),
reverse(Part,Reversed). % the rest of Xs is reversed in Ys
Now on to some sample queries... First, the original query you posted in the question:
?- list_singlePartReversed([1,3,2,4], [1,2,3,4]).
yes
Next, a simple case we expect to fail:
?- list_singlePartReversed([1,4,3,2],[]).
no
What about all possible ways to do the reversal?
?- list_singlePartReversed([1,2,3,4], Xs).
Xs = [2,1,3,4] ? ;
Xs = [3,2,1,4] ? ;
Xs = [4,3,2,1] ? ;
Xs = [1,3,2,4] ? ;
Xs = [1,4,3,2] ? ;
Xs = [1,2,4,3] ? ;
no
What if the first argument is not instantiated but the second one is?
?- list_singlePartReversed(Xs, [1,2,3,4]).
Xs = [2,1,3,4] ? ;
Xs = [3,2,1,4] ? ;
Xs = [4,3,2,1] ? ;
Xs = [1,3,2,4] ? ;
Xs = [1,4,3,2] ? ;
Xs = [1,2,4,3] ? ;
no
And what about the most general query? Do we get a fair enumeration of the infinite solution set?
?- list_singlePartReversed(Xs,Ys).
Xs = [_A,_B], Ys = [_B,_A], prolog:dif([_A,_B],[_B,_A]) ? ;
Xs = [_A,_B,_C], Ys = [_B,_A,_C], prolog:dif([_A,_B,_C],[_B,_A,_C]) ? ;
Xs = [_A,_B,_C], Ys = [_C,_B,_A], prolog:dif([_A,_B,_C],[_C,_B,_A]) ? ;
Xs = [_A,_B,_C], Ys = [_A,_C,_B], prolog:dif([_A,_B,_C],[_A,_C,_B]) ? ;
Xs = [_A,_B,_C,_D], Ys = [_B,_A,_C,_D], prolog:dif([_A,_B,_C,_D],[_B,_A,_C,_D]) ? ...
Algorithmically, you can compare both from index 0, and find where they differ (call this index "a"), and compare backwards from n, until they differ (call this index "b").
Then reverse from index "a" to index "b" in one list, and compare the lists (or sub-list, it doesn't matter) to see if they are the same. If so, then true, otherwise false.
A corner case would be when both lists are identical. This could be be defined as either true or false, depending on whether a null set counts as a contiguous part of a list.
Search forward for mismatch:
[1,2,3,4]
[1,3,2,4]
^-------a
Search backward for mismatch:
[1,2,3,4]
[1,3,2,4]
^-----b
Reverse sub-list from a to b in either list:
[1,3,2,4] <-- Reversed sub-list indexed from 1-2
[1,3,2,4]
If equal, then true.
Does that help? This assumes that there is a single reversed sub-list.
This is a Prologish way you can do it:
rev(X,Y) :-
append(X1,X2,X3),
append(X3,X4,X),
reverse(X2,X5),
append(X1,X5,X6),
append(X6,X4,Y),
!.
Examples:
?- rev([1,3,2,4],[1,2,3,4]).
true.
?- rev([1,4,3,2],[1,2,3,4]).
true.
Related
i´m trying to create a predicate that returns me the element of a list that contains a certain number given by me.
Example:
?- where_is_it( [ [1,2,3] , [1,2,7] , [4,5] , [8] ] , 7 , X ).
X=[1,2,7].
I am a relatively new prolog programmer so this is my code:
where_is_it([],_,[]).
where_is_it([H|T],Num,H):-
member([Num],H),!,
where_is_it(T,Num,[]).
Thank you very much
You could use if_/3 and memberd_t/2 from module reif in order to be more deterministic:
where_is_it([H|T], X, L) :-
if_(memberd_t(X,H), L=H, where_is_it(T, X, L)).
Here is an implementation using tmember/2:
where_is_it(InList, X, L):- tmember(check(X,L),InList).
check(X,L,L1,T):- if_( memberd_t(X,L1), (T = true, L = L1), T = false).
where_is_it(Xss, X, Xs) :-
member(Xs, Xss),
member(X, Xs).
Here is a version using only tmember/2 and (=)/3 without any explicit recursion:
where_is_it(Xss,X,Xs) :-
tmember(=(Xs),Xss),
tmember(=(X),Xs).
The query given by the OP works as expected:
?- where_is_it([[1,2,3],[1,2,7],[4,5],[8]],7,X).
X = [1,2,7] ? ;
no
Some of the features of this version: If the element occurs in more than one list (differs from version with if_/3 and memberd_t):
?- where_is_it([[1,2,3],[1,2,7],[4,5],[8]],1,X).
X = [1,2,3] ? ;
X = [1,2,7] ? ;
no
Multiple occurrences of the element in one list are matched only once (differs from version with member/2):
?- where_is_it([[1,2,3,1],[4,5],[8]],1,X).
X = [1,2,3,1] ? ;
no
Multiple occurrences of the same list are matched only once (differs from version with member/2):
?- where_is_it([[1,2,3],[1,2,3],[4,5],[8]],1,X).
X = [1,2,3] ? ;
no
Even with an open list (differs from version with member/2 as well as from version with if_/3 and memberd_t):
?- where_is_it([[1,2,3],[1,2,7],[4,5],[8],[1|_]],1,X).
X = [1,2,3] ? ;
X = [1,2,7] ? ;
X = [1|_A],
dif([1|_A],[1,2,3]),
dif([1|_A],[1,2,7]) ? ;
no
If the actual element is variable:
?- where_is_it([[1,2,3],[8]],Y,X).
X = [1,2,3],
Y = 1 ? ;
X = [1,2,3],
Y = 2 ? ;
X = [1,2,3],
Y = 3 ? ;
X = [8],
Y = 8 ? ;
no
The most general query (differs from version with member/2 (only slightly) as well as from version with if_/3 and memberd_t):
?- where_is_it(Xss,X,Xs).
Xs = [X|_A],
Xss = [[X|_A]|_B] ? ;
Xs = [_A,X|_B],
Xss = [[_A,X|_B]|_C],
dif(X,_A) ? ;
Xs = [_A,_B,X|_C],
Xss = [[_A,_B,X|_C]|_D],
dif(X,_B),
dif(X,_A) ? ;
...
With some constraints (differs from version with member/2 (only slightly) as well as from version with if_/3 and memberd_t):
?- Xss=[_,_],Xs=[_,_],where_is_it(Xss,X,Xs).
Xs = [X,_A],
Xss = [[X,_A],_B] ? ;
Xs = [_A,X],
Xss = [[_A,X],_B],
dif(X,_A) ? ;
Xs = [X,_A],
Xss = [_B,[X,_A]],
dif([X,_A],_B) ? ;
Xs = [_A,X],
Xss = [_B,[_A,X]],
dif(X,_A) ? ;
no
You should maybe read what your clauses say? You need maybe one clause which says, "If X is member of H, then H is solution":
where_is_it([H|_], X, H) :-
member(X, H).
and then you still need another clause that says that maybe you have a solution in the rest of the list:
where_is_it([_|T], X, H) :-
where_is_it(T, X, H).
Maybe this is enough for beginning?
Ok, let us look at your code. The first clause is fine, whatever we are looking for it is not in the empty list.
where_is_it([],_,[]).
This is your second clause:
where_is_it([H|T],Num,H):-
member([Num],H),!,
where_is_it(T,Num,[]).
Here we have several problems:
First, instead of member([Num],H) you probably need member(Num,H) expressing that Num is an element of the list H.
Second, If this is the clause for the cases where Num is a member of H, your recursion should be as follows:
where_is_it([H|T],Num,[H|Found]):-
member(Num,H),!,
where_is_it(T,Num,Found).
This clause now expresses that whenever Num is a member of H, H belongs to our solution list and we have to look for further solutions in the tail of our list (that is in T) and collect them in Found.
You need an additional clause for the case that Num is not a member of H:
where_is_it([H|T],Num,Found):-
where_is_it(T,Num,Found).
This clause does not change your list of found solutions.
Hence the full code is:
where_is_it([],_,[]).
where_is_it([H|T],Num,[H|Found]):-
member(Num,H),!,
where_is_it(T,Num,Found).
where_is_it([_H|T],Num,Found):-
where_is_it(T,Num,Found).
I found a similar post but it didn't work for me. So please don't try to redirect me to other links.
This is the result that I want:
removeadj([a,a,a,b,c,c,a,a],[a,b,c,a]).
>True.
I tried this code:
concatener([],R,R).
concatener([X|Y],L,R):-concatener(Y,L,R1),R=[X|R1].
removeadj([],[]).
removeadj([X],[X]).
removeadj([X|Y],R):- Y=[X|L],removeadj(Y,R1),concatener([],R1,R).
removeadj([X|Y],R):- removeadj(Y,R1),concatener(X,R1,R).
When I try a list with one element duplicated many times, it works:
removeadj([a,a,a,a,a],[a]).
> True
But when I use different elements, it doesn't work:
removeadj([a,a,a,b],[a,b]).
> False.
I do not see where the problem is, so I can't fix it. Please I need your help.
Relational names
The first is to reconsider the name of your relation. Currently, it suggests that someone has to do something. removeadj that's a command. Quite an adequate name in a programming language where commands are the ruling metaphor. But not in Prolog.
In Prolog, we have relations. A name that reflects this relationness is often very helpful. Why not list_list/2? After all, your relation is about two lists! OK, maybe that name was a bit too general. What about list__list_without_adjacent_elements/2? Lengthy, but relational. Maybe we shorten that to: list_noadjs/2. Note the s at the end: That means: it's a plural which means it's a list.
Observe properties
Before thinking about "doing" this or that. Rather muse about concrete examples - preferably ground examples, as you have given them. And about other properties. One observation is that all elements of the second list will be there in the first. In fact not only that. But they will also occur in the same order. Let me try to formulate that. Of course, that observation is not sufficient to write an entire predicate. But here comes the cool thing in Prolog: We do not need to implement everything. We can start with gross generalizations that contain all what we want plus some more.
Start with a too general definition.
To show you the most extreme, let's try:
list_noadjs(_Xs, _Ys).
This is the mother of all binary relations! That definition always succeeds, no matter what. Evidently, we will have to specialize it. Say, by looking at the second argument which is a list:
list_noadjs(_Xs, []).
list_noadjs(_Xs, [Y|Ys]) :-
list_noadjs(_, Ys).
If the list is [] so will be the original one. And both start with the same element!
list_noadjs(Xs, []) :-
Xs = [].
list_noadjs(Xs, [Y|Ys]) :-
Xs = [Y|_],
list_noadjs(_, Ys).
or more compactly:
list_noadjs([], []).
list_noadjs([Y|_Xs], [Y|Ys]) :-
list_noadjs(_, Ys).
Now, the first list contains elements of the second list. And in between something else:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
list_(Xs0,Xs1),
list_noadjs(Xs1, Ys).
list_(Xs,Xs).
list_([_|Xs0],Xs) :-
list_(Xs0,Xs).
Is this already our relation? Let's give it a try:
?- list_noadjs("aaab",Ys).
Ys = "aaab"
; Ys = "aaa"
; Ys = "aab"
; Ys = "aa"
; Ys = "aab"
; Ys = "aa"
; Ys = "ab" % <===== * RRRIGHT !!!!***
; Ys = "a"
; false.
(Btw. I am using library(double_quotes) to make answers more readable.)
So we do have the expected solution. Alas, there are many incorrect solutions, too! We will have to continue to specialize this program:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
eq_list_(Y, Xs0,Xs1),
list_noadjs(Xs1, Ys).
eq_list_(_, Xs,Xs).
eq_list_(Y, [Y|Xs0],Xs) :-
eq_list_(Y, Xs0,Xs).
Now this is much better, but still not perfect:
?- list_noadjs("aaab",Ys).
Ys = "aaab"
; Ys = "aab"
; Ys = "aab"
; Ys = "ab" % !!! Right
; false.
We have to further specialize the program: After a sequence of identical elements, there must be something else:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
eq_list_(Y, Xs0,Xs1),
nohead(Xs1, Y),
list_noadjs(Xs1, Ys).
eq_list_(_, Xs,Xs).
eq_list_(Y, [Y|Xs0],Xs) :-
eq_list_(Y, Xs0,Xs).
nohead([], _X).
nohead([X|_], Y) :-
dif(X, Y).
So that's our relation.
Enjoy the relation!
Seriously. Don't just use the test cases you had. You have now a relation! That's not a function in disguise, it is truly more than that. Try it out! Ask completely unusual things, like:
?- list_noadjs(Xs,"abc").
Xs = "abc"
; Xs = "abcc"
; Xs = "abccc"
; Xs = "abcccc"
; ... .
So here we ask: Which lists correspond to "abc"? Note that only c is repeated! All the other solutions are hidden behind this wall of infinity. But we can play a little trick to get them:
?- length(Xs,N), list_noadjs(Xs,"abc").
Xs = "abc", N = 3
; Xs = "abcc", N = 4
; Xs = "abbc", N = 4
; Xs = "aabc", N = 4
; Xs = "abccc", N = 5
; Xs = "abbcc", N = 5
; Xs = "abbbc", N = 5
; Xs = "aabcc", N = 5
; Xs = "aabbc", N = 5
; Xs = "aaabc", N = 5
; Xs = "abcccc", N = 6
; ... .
Don't shy away from non-termation.
We have already seen it: Very often, we get infinitely many solutions. And (I must admit) it's even worse: Sometimes, our relations do not even terminate.
Here is one such example. Say, is there a way that the list in the second argument contains duplicates? Or that the following holds:
?- list_noadjs(Xs,[X,X]).
loops.
Prolog answers: mumble, mumble, lemme see...
To master Prolog, you will have to understand this in detail. But for the moment, there is often a good way out:
Specialize queries to get termination
So instead of asking: Is there any term that may correspond to [X,X] we may ask: Is there a list of size 5 (or any other finite size). Now Prolog denies this:
?- Xs = [_,_,_,_,_], list_noadjs(Xs,[X,X]).
false.
That's not the universal answer you wanted. But it is better than nothing.
Sometimes all these queries are too much for you. Let Prolog do the thinking for you by:
Enumerating all solutions
Often, this is very simple. Start with the most general query. The big advantage here is that no thinking is required on your side at all. And still you look like a pro:
?- list_noadjs(Xs,Ys).
Xs = [], Ys = []
; Xs = [_A], Ys = [_A]
; Xs = [_A,_B], Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_B,_C], Ys = [_A,_B,_C], dif(_B,_A), dif(_C,_B) ?
; Xs = [_A,_B,_C,_D], Ys = [_A,_B,_C,_D], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; Xs = [_A,_B,_C,_D,_E], Ys = [_A,_B,_C,_D,_E], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; ... .
What we got here are so called answers. A single answer may contain infinitely many solutions. Think of this: You are looking at infinity! Some conditions (called constraints) must hold, like dif/2. But that's it.
The third answer was:
Xs = [_A,_B], Ys = [_A,_B], dif(_B,_A)
So Xs and Ys are the same list with two distinct elements. So this answer implies Xs = "ab", Ys = "ab" but also Xs = [1,2], Ys = [1,2] and many, many more.
Even better, enumerate all answers in a systematic ("fair") manner:
?- length(Xs, N), list_noadjs(Xs,Ys).
Xs = [], N = 0, Ys = []
; Xs = [_A], N = 1, Ys = [_A]
; Xs = [_A,_B], N = 2, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A], N = 2, Ys = [_A]
; Xs = [_A,_B,_C], N = 3, Ys = [_A,_B,_C], dif(_B,_A), dif(_C,_B)
; Xs = [_A,_B,_B], N = 3, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A,_B], N = 3, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A,_A], N = 3, Ys = [_A]
; Xs = [_A,_B,_C,_D], N = 4, Ys = [_A,_B,_C,_D], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; ... .
These are all solutions up to length 3. There are no other! We know this for sure because the last answer is already of size 4. So all solutions below are here already!
Often looking at such answers is very helpful. For example, it permits you to rapidly detect errors (like in the other answer that was given previously). So, don't tell nobody about that trick.
The last clause in the predicate still has an issue:
removeadj([X|Y], [X|R1]):- removeadj(Y, R1).
In the event that the element following X is also X, X is still carried in the head of the second argument. This clause must check if the following element is different before allowing it in the second argument:
removeadj([X,Y|L], [X|R]) :-
dif(X,Y),
removeadj([Y|L], R).
Here, Y is at the head of the second list only if it's different from X.
So the whole solution looks like:
removeadj([], []).
removeadj([X], [X]).
removeadj([X,X|T], R) :- % drop an X if next element is X
removeadj([X|T], R).
removeadj([X,Y|T], [X|R]) :- % carry X along if next element different
dif(X,Y),
removeadj([Y|T], R).
removeadj([],[]).
removeadj([X],[X]).
removeadj([X|Y],R):- Y=[X|L],removeadj(Y,R1),R=R1.
removeadj([X|Y],[X|R1]):- removeadj(Y,R1).
The aim is to implement the predicate noDupl/2.
The first argument of this predicate is the list to analyze and second argument is the list of numbers which are no duplicate.
I could not understand code below and when I compiled it, it gave an error message that contained is undefined procedure, however as a hint it is written that we can use as predefined predicate contained and notContained. I think I need to define contained and notContained.
noDupl(XS, Res):-
help( XS, [],Res).
help([],_,[]).
help([X|XS],Seen,[X|Res]):-
notContained(X,XS),
notContained(X,Seen),
help(XS, [X|Seen], Res).
help([X|XS],Seen,Res):-
contained(X,Seen),
help(XS, Seen, Res).
help([X|XS],Seen,Res):-
contained(X,XS),
help(XS, [X|Seen], Res).
Could someone please explain me the problem.
The missing definitions might be:
contained(X,[X|_]).
contained(X,[E|Es]) :-
dif(X, E),
contained(X, Es).
notContained(_X, []).
notContained(X, [E|Es]) :-
dif(X, E),
notContained(X, Es).
(I like to call these relations rather memberd/2 and non_member/2.)
The definition you gave extends the relation with an extra argument for the elements considered so far.
To understand the meaning of each clause, read each right-to-left in the direction of the arrow (the :- is a 1970's ASCII-fication of ←). Let's take the first rule:
Provided, that X is not an element of XS, and
provided, that X is not an element of Seen, and
provided, that help(X, [X|Seen], Res) is true,
then also help([X|XS],Seen,[X|Res]) is true.
In other words, if X is neither in the list of visited elements Seen nor in the elements yet to be visited XS, then it does not possess a duplicate.
What is a bit difficult to understand is whether or not the clauses you gave are mutually exclusive - this is, strictly speaking, not your concern, as long as you are only interested in declarative properties, but it is a good idea to avoid such redundancies.
Here is a case, where such redundancy shows:
?- noDupl([a,a,a],U).
U = []
; U = []
; false.
Ideally, the system would give one determinate answer:
?- noDupl([a,a,a], U).
U = [].
Personally, I do not like a lot to split things into too many cases. Essentially, we could have two: it is a duplicate, and it is none.
It is possible to provide a definition that is correct and still fully determinate for the cases where determinism is possible - such as when the first argument is "sufficiently instantiated" (which includes a ground list). Let's see if there are some answers into that direction.
I've annotated your code for you:
noDupl( XS , Res ) :- % Res is the [unique] set of element from the bag XS
help( XS, [],Res) % if invoking the helper succeeds.
. %
help( [] , _ , [] ) . % the empty list is unique.
help( [X|XS] , Seen , [X|Res] ) :- % A non-empty list is unique, if...
notContained(X,XS), % - its head (X) is not contained in its tail (XS), and
notContained(X,Seen), % - X has not already been seen, and
help(XS, [X|Seen], Res). % - the remainder of the list is unique.
help( [X|XS] , Seen , Res ) :- % otherwise...
contained(X,Seen) , % - if X has been seen,
help(XS, Seen, Res). % - we discard it and recurse down on the tail.
help([X|XS],Seen,Res):- % otherwise...
contained(X,XS), % - if X is in the tail of the source list,
help(XS, [X|Seen], Res). % - we discard it (but add it to 'seen').
Your contained/2 and notContained/2` predicates might be defined as this:
contained( X , [X|_] ) :- ! .
contained( X , [Y|Ys] ) :- X \= Y , contained( X , Ys ) .
not_contained( _ , [] ) .
not_contained( X , [Y|Ys] ) :- X \= Y , not_contained(X,Ys) .
Now, I may be missing something in your code, but there's an awful lot of redundancy in it. You could simply write something like this (using the built-ins member/2 and reverse/2):
no_dupes( List , Unique ) :- no_dupes( Bag , [] , Set ) .
no_dupes( [] , V , S ) . % if we've exhausted the bag, the list of visited items is our set (in reverse order of the source)
reverse(V,S) % - reverset it
. % - to put our set in source order
no_dupes( [X|Xs] , V , S ) :- % otherwise ...
( member(X,V) -> % - if X is already in the set,
V1 = V % - then we discard X
; V1 = [X|V] % - else we add X to the set
) , % And...
no_dupes( Xs , V1 , S ) % we recurse down on the remainder
. % Easy!
Can this be done in a pure and efficient way?
Yes, by using
tpartition/4 and (=)/3 like so:
dups_gone([] ,[]).
dups_gone([X|Xs],Zs0) :-
tpartition(=(X),Xs,Ts,Fs),
if_(Ts=[], Zs0=[X|Zs], Zs0=Zs),
dups_gone(Fs,Zs).
Some sample ground queries (all of which succeed deterministically):
?- dups_gone([a,a,a],Xs).
Xs = [].
?- dups_gone([a,b,c],Xs).
Xs = [a, b, c].
?- dups_gone([a,b,c,b],Xs).
Xs = [a, c].
?- dups_gone([a,b,c,b,a],Xs).
Xs = [c].
?- dups_gone([a,b,c,b,a,a,a],Xs).
Xs = [c].
?- dups_gone([a,b,c,b,a,a,a,c],Xs).
Xs = [].
This also works with more general queries. Consider:
?- length(Xs,N), dups_gone(Xs,Zs).
N = 0, Xs = [], Zs = []
; N = 1, Xs = [_A], Zs = [_A]
; N = 2, Xs = [_A,_A], Zs = []
; N = 2, Xs = [_A,_B], Zs = [_A,_B], dif(_A,_B)
; N = 3, Xs = [_A,_A,_A], Zs = []
; N = 3, Xs = [_A,_A,_B], Zs = [_B], dif(_A,_B)
; N = 3, Xs = [_A,_B,_A], Zs = [_B], dif(_A,_B)
; N = 3, Xs = [_B,_A,_A], Zs = [_B], dif(_A,_B), dif(_A,_B)
; N = 3, Xs = [_A,_B,_C], Zs = [_A,_B,_C], dif(_A,_B), dif(_A,_C), dif(_B,_C)
; N = 4, Xs = [_A,_A,_A,_A], Zs = []
...
Given a list eg [1,2,3,7,2,5,8,9,3,4] how would I extract the sequences within the list?
A sequence is defined as an ordered list (Normally I would say n-tuple but I have been told in prolog a tuple is referred to as a sequence). So we want to cut the list at the point where the next element is smaller than the previous one.
So for the list [1,2,3,7,2,5,8,9,3,4] it should return:
[ [1,2,3,7], [2,5,8,9], [3,4] ] %ie we have cut the list at position 4 & 8.
For this exercise you CANNOT use the construct ; or ->
Many thanks in advance!
EXAMPLE RESULTS:
eg1.
?-function([1,2,3,7,2,5,8,9,3,4],X): %so we cut the list at position 4 & 9
X = [ [1,2,3,7], [2,5,8,9], [3,4] ].
eg2.
?-function([1,2,3,2,2,3,4,3],X): %so we cut the list at position 3,4 & 8
X = [ [1,2,3], [2], [2,3,4], [3] ].
Hopefully that helps clarify the problem. If you need further clarification just let me know! Thanks again in advance for any help you are able to provide.
First, let's break it down conceptually. The predicate list_ascending_rest/3 defines a relation between a list Xs, the left-most ascending sublist of maximum length Ys, and the remaining items Rest. We will use it like in the following query:
?- Xs = [1,2,3,7,2,5,8,9,3,4], list_ascending_rest(Xs,Ys,Rest).
Ys = [1,2,3,7],
Rest = [2,5,8,9,3,4] ;
false.
The straight-forward predicate definition goes like this:
:- use_module(library(clpfd)).
list_ascending_rest([],[],[]).
list_ascending_rest([A],[A],[]).
list_ascending_rest([A1,A2|As], [A1], [A2|As]) :-
A1 #>= A2.
list_ascending_rest([A1,A2|As], [A1|Bs], Cs) :-
A1 #< A2,
list_ascending_rest([A2|As], Bs,Cs).
Then, let's implement predicate list_ascendingParts/2. This predicate repeatedly uses list_ascending_rest/3 for each part until nothing is left.
list_ascendingParts([],[]).
list_ascendingParts([A|As],[Bs|Bss]) :-
list_ascending_rest([A|As],Bs,As0),
list_ascendingParts(As0,Bss).
Example queries:
?- list_ascendingParts([1,2,3,7,2,5,8,9,3,4],Xs).
Xs = [[1,2,3,7], [2,5,8,9], [3,4]] ;
false.
?- list_ascendingParts([1,2,3,2,2,3,4,3],Xs).
Xs = [[1,2,3], [2], [2,3,4], [3]] ;
false.
Edit 2015/04/05
What if the ascending parts are known but the list is unknown? Let's find out:
?- list_ascendingParts(Ls, [[3,4,5],[4],[2,7],[5,6],[6,8],[3]]).
Ls = [3,4,5,4,2,7,5,6,6,8,3] ? ;
no
And let's not forget about the most general query using list_ascendingParts/2:
?- assert(clpfd:full_answer).
yes
?- list_ascendingParts(Ls, Ps).
Ls = [], Ps = [] ? ;
Ls = [_A], Ps = [[_A]] ? ;
Ls = [_A,_B], Ps = [[_A],[_B]], _B#=<_A, _B in inf..sup, _A in inf..sup ? ...
Edit 2015-04-27
Room for improvement? Yes, definitely!
By using the meta-predicate splitlistIfAdj/3 one can "succeed deterministically" and "use non-determinism when required", depending on the situation.
splitlistIfAdj/3 is based on if_/3 as proposed by #false in this answer. So the predicate passed to it has to obey the same convention as (=)/3 and memberd_truth/3.
So let's define (#>)/3 and (#>=)/3:
#>=(X,Y,Truth) :- X #>= Y #<==> B, =(B,1,Truth).
#>( X,Y,Truth) :- X #> Y #<==> B, =(B,1,Truth).
Let's re-ask above queries, using splitlistIfAdj(#>=)
instead of list_ascendingParts:
?- splitlistIfAdj(#>=,[1,2,3,7,2,5,8,9,3,4],Pss).
Pss = [[1,2,3,7],[2,5,8,9],[3,4]]. % succeeds deterministically
?- splitlistIfAdj(#>=,[1,2,3,2,2,3,4,3],Pss).
Pss = [[1,2,3],[2],[2,3,4],[3]]. % succeeds deterministically
?- splitlistIfAdj(#>=,Ls,[[3,4,5],[4],[2,7],[5,6],[6,8],[3]]).
Ls = [3,4,5,4,2,7,5,6,6,8,3] ; % works the other way round, too
false. % universally terminates
Last, the most general query. I wonder what the answers look like:
?- splitlistIfAdj(#>=,Ls,Pss).
Ls = Pss, Pss = [] ;
Ls = [_G28], Pss = [[_G28]] ;
Ls = [_G84,_G87], Pss = [[_G84],[_G87]], _G84#>=_G87 ;
Ls = [_G45,_G48,_G41], Pss = [[_G45],[_G48],[_G41]], _G45#>=_G48, _G48#>=_G41
% and so on...
maplist/3 as suggested in the comment won't help you here because maplist/3 is good at taking a list and mapping each element into a same-size collection of something else, or establishing a relation evenly across all of the individual elements. In this problem, you are trying to gather contiguous sublists that have certain properties.
Here's a DCG solution. The idea here is to examine the list as a series of increasing sequences where, at the boundary between them, the last element of the prior sequence is less than or equal to the first element of the following sequence (as the problem statement basically indicates).
% A set of sequences is an increasing sequence ending in X
% followed by a set of sequences that starts with a value =< X
sequences([S|[[Y|T]|L]]) --> inc_seq(S, X), sequences([[Y|T]|L]), { X >= Y }.
sequences([S]) --> inc_seq(S, _).
sequences([]) --> [].
% An increasing sequence, where M is the maximum value
inc_seq([X,Y|T], M) --> [X], inc_seq([Y|T], M), { X < Y }.
inc_seq([X], X) --> [X].
partition(L, R) :- phrase(sequences(R), L).
| ?- partition([1,2,3,4,2,3,8,7], R).
R = [[1,2,3,4],[2,3,8],[7]] ? ;
(1 ms) no
| ?- partition([1,2,3,2,2,3,4,3],X).
X = [[1,2,3],[2],[2,3,4],[3]] ? ;
(1 ms) no
The only reason for the rule sequences([]) --> []. is if you want partition([], []) to be true. Otherwise, the rule isn't required.
how does the following code for prefix in Prolog work?
prefix supposed to return the prefix of a list.
for example:
?-prefix([1,2,3],X)
(i am not sure if it should be written as the opposite: prefix(X, [1,2,3])
will return [], then [1], then [1,2]...
the code:
prefix(Xs,Ys):- append(Xs,_,Ys).
append([],Xs,Xs).
append([X|Xs],Ys,[X,Zs]):-append(Xs,Ys,Zs).
could anyone run an example ? (the append just adds two strings)
The correct way to write the predicate would be:
prefix(Xs, Ys) :- append(Ys, _, Xs).
What this says is Ys is a prefix of Xs if Ys appended with some other list (I don't care what it is) is Xs.
Now in prolog, when you issue a query, such as append, and the predicate is defined relationally, it will attempt to find all solutions that make the query TRUE. If I have the query, append(Ys, _, [1,2,3]), prolog can make this true by instantiating Ys with [] and _ with [1,2,3] (thus yielding the solution, Ys = []. It will then backtrack and find another solution: Ys is [1] and _ is [2,3]. And so on...
So when you run your query, you get:
| ?- prefix([1,2,3], X).
X = [] ? ; % append([], [1,2,3], [1,2,3]); X = [], _ = [1,2,3]
X = [1] ? ; % append([1], [2,3], [1,2,3]); X = [1], _ = [2,3]
X = [1,2] ? ; % append([1,2], [3], [1,2,3]); X = [1,2], _ = [3]
X = [1,2,3] % append([1,2,3], [], [1,2,3]); X = [1,2,3], _ = []
Remember, Prolog seeks to find solutions by checking facts and rules and instantiating variables. If there's more than one way to instantiate the variables, it will backtrack. If backtracking finds another set of variable instantiations that makes the query true, Prolog will show it as a solution. If more backtracking is available, it prompts you for more (and entering ; says to give me the next one).