Prolog: sort sublist in list - list

How to make it?
I need something like this:
?- elsort([d,s,a,[r,t,h]],X).
X = [a, d, s, [h, r, t]].
But i get :
?- elsort([d,s,a,[r,t,h]],X).
X = [a, d, s, [r, t, h]].
My code:
elsort([],[]).
elsort([A|B],C):-
elsort(B,D),
elsortx(A,D,C).
elsortx(A,[X|B],[X|C]):-
order(X,A),
!,
elsortx(A,B,C).
elsortx(A,B,[A|B]).
order(A,A2):-
A #< A2.
thanks for helping.

I would probably make the sorting of sublists a two-phase operation. First, iterate over your source list, sorting each sublist found. When that's done, then actually sort the final list. The rationale is to avoid the repeated sorting of sublists.
Something like this:
my_sort( Unsorted , Sorted ) :- % to sort a list of lists...
sort_sublists( Unsorted, U ) , % - 1st pass: sort any sublists
merge_sort( U , Sorted ) % - 2nd pass: actually sort the results
. %
sort_sublists( [] , [] ) . % an empty list has no sublists to sort
sort_sublists( [X|Xs] , [Y|Ys] ) :- % otherwise...
list(X) , % - if the head is a list
!, % - eliminate the choice point
my_sort(X,Y) % - and sort the head (along with its sublists)
. %
sort_sublists( [X|Xs] , [X|Ys] ). % otherwise (the head is not a list)
merge_sort( [] , [] ) . % an empty list is sorted.
merge_sort( [A] , [A] ) . % list of 1 item is sorted.
merge_sort( [A,B|Cs] , Sorted ) :- % otherwise, to sort a list of 2 or more items...
partition([A,B|Cs] , L , R ) , % - partition it into two halves.
merge_sort( L , L1 ) , % - then recursively sort the left half
merge_sort( R , R1 ) , % - and recursively sort the right half
merge( L1 , R1 , Sorted ) % - then merge the two now-order halves together
. % producing the final, sorted list
partition( [] , [] , [] ) .
partition( [L] , [L] , [] ) .
partition( [L,R|Xs] , [L|Ls] , [R|Rs] ) :- partition(Xs,Ls,Rs) .
merge( [] , [] , [] ) .
merge( [L] , [] , [L] ) .
merge( [] , [R] , [R] ) .
merge( [L|Ls] , [R|Rs] , [R,L|Xs] ) :-
compare(CC,L,R) ,
swap(CC,L,R,Lo,Hi),
merge(Ls,Rs,Xs)
.
swap( < , L , R , L , R ) .
swap( > , L , R , R , L ) .
swap( = , L , R , L , R ) .
list( X ) :- var(X) , ! , fail .
list( [] ) .
list( [_|_] ) .
Note that compare/3 is a built-in predicate that compares two terms in the standard order of things and returns an atom, one of <, = , >, with each having the obvious meaning. You could roll your own if you'd like:
compare(<,X,Y) :- X #< Y .
compare(>,X,Y) :- X #> Y .
compare(=,X,Y) :- X == Y .

You would need to sort the element itself if it is a list, e.g.:
elsort([A|B],C):-
elsort(B,D),
(is_list(A)->elsort(A, SA);SA=A),
elsortx(SA,D,C).
Sample input:
?- elsort([d,s,a,[r,t,h]],X).
X = [a, d, s, [h, r, t]].

Related

Returning false if item is not in list (PROLOG)

Below is the code to remove a key (A) from a list if it is there. If it isn't there, it currently returns the entire list. I would like it instead to return 'false.' Sample outputs will be below as well.
mySelect(_, [], []).
mySelect(X, [Y|K], [Y|M]):- mySelect(X, K, M), (X \= Y).
mySelect(X, [X|K], R) :- mySelect(X, K, R).
Currently this will output:
?- my_delete(c,[a,b,c,d],R).
R = [a, b, d] .
?- my_delete(e,[a,b,c,d],R).
R = [a, b, c, d] .
I would like it to output:
?- my_delete(c,[a,b,c,d],R).
R = [a, b, d] .
?- my_delete(e,[a,b,c,d],R).
false .
Any pointers would be greatly appreciated!
You can add another rule which first ensures that X is a member of L, before applying mySelect.
delete(X, L, R) :- member(X, L), mySelect(X, L, R).
Either partition the list into matches and non-matches:
filter( X , Ys, Zs ) :- filter(X,Ys,[_|_],Zs).
filter( _ , [] , [] , [] ) .
filter( X , [X|Ys] , [X|Xs] , Zs ) :- !, filter(X,Ys,Xs,Zs) .
filter( X , [Y|Ys] , Xs , [Y|Zs] ) :- filter(X,Ys,Xs,Zs) .
or count the number of items and test the count for being greater than zero:
filter( X , Ys, Zs ) :- filter(X,Ys,0,N,Zs), N > 0 .
filter( _ , [] , N , N , [] ) .
filter( X , [X|Ys] , T , N , Zs ) :- T1 is T+1, !, filter(X,Ys,T1,N, Zs) .
filter( X , [Y|Ys] , T , N , [Y|Zs] ) :- !, filter(X,Ys,T,N,Zs) .

Prolog: How to get a copy of the original list in a recursion

I am trying to get the original list from List and store it to Temp. But List gets updated during recursions so Temp can't get the original data of List before recursions. Need help on how to get the original list with the sorted sub lists so I can use them in printPerMerge. Thank you!
mSort(Sorted,List) :-
length(List, N),
copy_term(List, Temp),
write('Temp '), tab(1),
format('~w ',[Temp]), nl,
FLength is //(N, 2),
SLength is N - FLength,
length(FUnsorted, FLength),
length(SUnsorted, SLength),
append(FUnsorted, SUnsorted, List),
mSort(FSorted, FUnsorted),
mSort(SSorted, SUnsorted),
combine(Sorted, FSorted, SSorted),
printPerMerge(Sorted, Temp),
write('Sorted '), tab(1),
format('~w ',[Sorted]), nl.
It looks like you are trying to implement a merge sort. With a merge sort, there's really no need to look at list length, or pre-construct lists of the desired length.
Try something like this:
merge_sort( [] , [] ) . % The empty list is ordered, is it not?
merge_sort( [X] , [X] ) . % A list of length 1 is ordered, is it not?
merge_sort( [X,Y|Zs] , Sorted ) :- % A list of length > 1 is not [necessarily] sorted, so....
partition([X,Y|Zs], Ls, Rs) , % - Split the unordered list into two lists of equal length
merge_sort(Ls,L1) , % - Recursively sort one sublist
merge_sort(Rs,R1) , % - Recursively sort the other sublist
merge(L1,R1,Sorted) % - And merge them together, in order
. % Easy!
partition( [] , [] , [] ) .
partition( [X] , [X] , [] ) .
partition( [X,Y|Zs] , [X|Xs] , [Y|Ys] ) :- partition(Zs,Xs,Ys).
merge( [] , [] , [] ) .
merge( [X|Xs] , [] , [X|Zs] ) :- merge(Xs,[],Zs) .
merge( [] , [Y|Ys] , [Y|Zs] ) :- merge([],Ys,Zs) .
merge( [X|Xs] , [Y|Ys] , [X|Zs] ) :- X #< Y, !, merge(Xs, [Y|Ys], Zs ) .
merge( [X|Xs] , [Y|Ys] , [Y|Zs] ) :- merge([X|Xs], Ys, Zs) .

Prolog. I can't mix two lists

I want to mix two lists into one. For example, [1,3,5] and [2,3,9] would yield [1,2,3,5,9].
I tried this:
mezclar( L1, L2, L3 ):-
L1 = [Cab|Cola] ,
L3 = [Cab,Cola2] ,
mezclar(L2,Cola,Cola2) .
mezclar( L1, L2, L3 ):-
L1=[] ,
L3=L2 .
But I have 2 problems.
The first problem are duplicated numbers
The second one is that I'm putting lists into the list and I dont want to.
If I execute
mezclar( [1,3,5], [2,5,9], X ).
I get
X = [1, [2, [3, [5, [5|...]]]]]
To mix two lists into one, with the resulting list being ordered and without duplicates, try:
mezclar(L1,L2,L3) :- append(L1,L2,L4), sort(L4,L3).
The query:
mezclar([1,3,5], [2,5,9], X).
will produce the result:
X = [1, 2, 3, 5, 9]
This example uses sort/2. Here is a link to the SWI documentation for sort/2:
http://eu.swi-prolog.org/pldoc/man?predicate=sort/2
Are input lists sorted? If so you merge them pretty fast.
merge([], Xs, Xs).
merge([X|Xs], [], [X|Xs]).
merge([X|Xs], [Y|Ys], [X|Zs]) :- X < Y, merge(Xs, [Y|Ys], Zs).
merge([X|Xs], [Y|Ys], [Y|Zs]) :- X > Y, merge([X|Xs], Ys, Zs).
merge([X|Xs], [X|Ys], [X|Zs]) :- merge(Xs, Ys, Zs).
Assuming that your lists are ordered, then it would seem that you are looking at a straight merge. And that's simple:
merge( [] , [] , [] ) . % merging two empty lists yields the empty list itself
merge( [] , [Y|Ys] , [Y|Ys] ) . % merging the empty list with a non-empty list yields the non-empty list
merge( [X|Xs] , [] , [X|Xs] ) . % merging a non-empty list with the empty list yields the non-empty list
merge( [X|Xs] , [Y|Ys] , [X,Y|Zs] ) :- % otherwise, when both lists are non-empty...
X #= Y , % - if X and Y compare as equal (in the standard order of terms)
merge( Xs, Ys, Zs ) . % - Take both X and Y and recurse down on the tail(s).
merge( [X|Xs] , [Y|Ys] , [X|Zs] ) :- % otherwise, when both lists are non-empty...
X #< Y , % - if X compares low to Y (in the standard order of terms)
merge( Xs, [Y|Ys], Zs ) . % - Take X and recurse down on the tail(s).
merge( [X|Xs] , [Y|Ys] , [X|Zs] ) :- % otherwise, when both lists are non-empty...
X #> Y , % - if X compares hight to Y (in the standard order of terms)
merge( [X|Xs], Ys, Zs ) . % - Take Y and recurse down on the tail(s).
The above will not eliminate duplicates from the set, nor will it unify any unbound variables. Merging [1,3,5] with [1,3,5] yields [1,1,3,3,5,5] as you would expect. If you want set-like semantics, you'll need to modify the last 3 clauses to use different comparision/unification operators.

Prolog double every other number in list

Hey guys I am trying to double every other number, but keep the rest of the list the same. Ex. [2,2,2,2] -> [2,4,2,4]
Here is what I have so far:
double_elements([H,H2|T], [X|Doubled_list]):-
X is H2*2,
double_elements(T, Doubled_list).
All it returns is [4,4].
What you've written is equivalent to
double_elements( A, B ) :-
A = [ H, H2 | T ],
B = [ X | Doubled_list ],
X is H2*2,
double_elements( T, Doubled_list ).
This means, you double each even-positioned element (H2) from the list A while putting the result X into the list B, but what about the elements in the odd positions, H?
You're just skipping those. To see it better, try your predicate with [1,3,5,7]. It will produce [6,14].
Instead, include those Hs in the "output" list B, as they are, without changing them.
You're missing a few cases:
The empty list
A list of length 1
Try something like this:
To double the odd elements of the list:
Where [1,2,3,4] becomes [2,2,6,4]
double_elements( [] , [] ) .
double_elements( [X] , [X2] ) :- X2 is X+X .
double_elements( [X,Y|Xs] , [X2,Y|Rs] ) :-
X2 is X+X,
double_elements(Xs,Rs)
.
To double the even elements of the list:
Where [1,2,3,4] becomes [1,4,3,8]
double_elements( [] , [] ) .
double_elements( [X] , [X] ) .
double_elements( [X,Y|Xs] , [X,Y2|Rs] ) :-
Y2 is Y+Y,
double_elements(Xs,Rs)
.

List exercise on prolog

So my professor at campus asked us to solve this exercise but is kinda tough and I am trying for 2 days now. Anyway here it is:
I'm given a list for example [a,b,c,d,w,n,i,c,k,a,b,c,d,w] and on this list I have to find out if there is "suspect" sublist. The sublist is considered "suspect" if
1) the same sublist is in the beginning and at the end,
2) it contains "w",
3) its length is 5.
The list I give has a "suspect" sublist.
If there is a suspect sublist the program must return the sublist or if there is not the program must return [o,k].
Any ideas will be welcome, thanks a lot!
(sorry if i posted something wrong)
EDIT
So after some help here is the asnwer:
checkMessage1(L,SL):-
suspiciousMessage1(L,SL).
checkMessage1(L,[o,k]):-
not(suspiciousMessage1(L,SL)).
suspiciousMessage1(L,SL):-
append(SL,Last,L),
append(_,SL,Last),
length(SL,5),
member(w,SL).
Prolog is a declarative language: describe the solution in terms of constraints and let the engine do the work.
We can determine membership in a list thus:
contains( [X|Xs] , X ) :- ! .
contains( [_|Xs] , X ) :- contains(Xs,X) .
We can determine if a lists 1st element is identical to its last element using the built-in predicate append/3:
list_starts_and_ends_with_identical( [X|Xs] ) :-
append( [X|_] , [X] , [X|Xs] )
.
Or, if you'd rather roll your own:
list_starts_and_ends_with_identical( [A|Xs] ) :-
list_ends_with( Xs , A )
.
list_ends_with( [A] , A ) .
list_ends_with( [B,C|D] , A ) :-
list_ends_with( [C|D] , A )
.
And we can enumerate sublists of the desired length like so:
sublist_of_length( Xs, L , Ys ) :- % to enumerate sublists of the desired length,
integer(L) , % - validate that the length is an integer, and
L > 0 , % - validate that the length is positive, and
length(Ys,L) , % - construct a list of unbound variables of the desired length, and
sl(Ys,L) % - invoke the helper
. %
sl( [X|Xs] , L ) :- % to enumerate sublists,
append( L , _ , [X|Xs] ) % - simply get the prefix of the desired length
. %
sl( [_|Xs] , L ) :- % on backtracking,
sl( Xs , L ) % - just recurse down on the tail of the list, discarding the first element.
.
Then, all we have to do is assemble the parts:
suspect_sublist( Xs , L ) :- % the source list Xs contains a suspect sublist L, IF...
sublist_of_length( Xs , 5 , L ) , % - L is a sublist of Xs having length 5, and
contains( L , w ) , % - L contains the atom 'w', and
list_starts_and_ends_with_identical( L ) , % - the first element of L is identical to the last.
. % Easy!
This is a good example for using DCGs:
list_suspect(Xs, Ys) :-
length(Ys, 5),
phrase(( seq(Ys), ..., seq(Ys) ), Xs),
phrase((...,"w",...), Ys).
... --> [] | [_], ... .
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
And here is a version using append/3 instead:
list_suspect(Xs, Ys) :-
Ys = [_,_,_,_,_],
append(Ys,Is, Xs),
append(_, Ys, Is).
append("w", _, W), % or: "w" = [C], W = [C|_]
append(_, W, Ys).
Is it better readable? I think not.
The part with the [o,k] looks a bit unnatural to me, but it would be:
list_ret(Xs, Ys) :-
list_suspect(Xs, Ys).
list_ret(Xs, Ys) :-
\+ list_suspect(Xs,_),
Ys = "ok".
one liner, using append/2
suspect(L, S) :-
length(S, 5), append([S,_,S], L), memberchk(w, S) -> true ; S = [o,k].
edit as noted by false, the definition is buggy (lacks steadfastness ?): an amended rule
suspect(L, R) :-
length(S, 5), append([S,_,S], L), memberchk(w, S) -> S = R ; R = [o,k].