Splitting a list in half [duplicate] - list

This question already has answers here:
Split a list in half
(9 answers)
Closed 5 years ago.
trying to write code that splits a list in half for even numbers of elements, eg. split([1,2,3,4],A,B) would give A = [1,2] and B = [3,4].
And behave like Java list.length()/2 would for odd numbers of elements, so split([1,2,3,4,5],A,B) would give A = [1,2] and B = [3,4,5].
The closest I can get is something like this, but this isn't quite right, and only works for even numbers.
split(L, A, B) :-
append(A, B, L),
length(A, N),
length(B, N).

One way to split a list in half in Prolog is to do so using append/3 and the same_length/2 predicates:
split(List, Left, Right) :-
same_length(Left, Right),
append(Left, Right, List).
split(List, Left, [R|Rs]) :-
same_length(Left, Rs),
append(Left, [R|Rs], List).
This gives you:
| ?- split(L, A, B).
A = B, B = L, L = [] ;
A = [_1038],
B = [_1048],
L = [_1038, _1048] ;
A = [_1038, _1044],
B = [_1054, _1060],
L = [_1038, _1044, _1054, _1060] ;
A = [_1038, _1044, _1050],
B = [_1060, _1066, _1072],
L = [_1038, _1044, _1050, _1060, _1066, _1072] ;
...

Related

Prolog - get middle element of List

I would like to get the middle element of a list in Prolog.
The predicates middle([1,2,3],M) and middle([1,2,3,4],M) should both return 2 as a result.
And I am allowed to use the predicate deleteLast.
I know that there are similar posts that solve that question but I have not found one that just uses deleteLast.
Even the syntax is not correct - however this is my solution so far:
middle([], _).
middle([X|XTail|Y], E) :-
1 is mod(list_length([X|XTail|Y], 2)),
middle([XTail], E).
middle([X|XTail|Y], E) :-
0 is mod(list_length([X|XTail|Y], 2)),
middle([X|XTail], E).
middle([X], X).
Question: Is that partly correct or am I completely on the wrong path ?
Sorry, the attempted solution you have is completely on the wrong path.
It doesn't use deleteLast/2 as you stated you require
You are using list_length/2 as if it were an arithmetic function, which it is not. It's a predicate.
You have a term with invalid syntax and unknown semantics, [X|XTail|Y]
In Prolog, you just need to think about it in terms of the rules. Here's an approach using deleteLast/2:
middle([X], X). % `X` is the middle of the single element list `[X]`
middle([X,_], X). % `X` is the middle of the two-element list `[X,_]`
% X is the middle of the list `[H|T]` if X is the middle of the list TWithoutLast
% where TWithoutLast is T with its last element removed
%
middle([H|T], X) :-
deleteLast(T, TWithoutLast),
middle(TWithoutLast, X).
I assume deleteLast/2 is well-behaved and just fails if T is empty.
You can also do this with same_length/2 and append/3, but, alas, doesn't use deleteLast/2:
middle(L, M) :-
same_length(L1, L2),
append(L1, [M|L2], L).
middle(L, M) :-
same_length(L1, L2),
append(L1, [M,_|L2], L).
So much unnecessary work, and unnecessary code. length/2 is very efficient, and a true relation. Its second argument is guaranteed to be a non-negative integer. So:
middle(List, Middle) :-
List = [_|_], % at least one element
length(List, Len),
divmod(Len, 2, Q, R), % if not available do in two separate steps
N is Q + R,
nth1(N, List, Middle).
And you are about ready:
?- middle(L, M), numbervars(L).
L = [A],
M = A ;
L = [A, B],
M = A ;
L = [A, B, C],
M = B ;
L = [A, B, C, D],
M = B ;
L = [A, B, C, D, E],
M = C ;
L = [A, B, C, D, E, F],
M = C .
I understand that this doesn't solve your problem (the answer by #lurker does) but it answers your question. :-(
Here is my attempt:
middle(L,M):- append(L1,L2,L),length(L1,N),length(L2,N), reverse(L1,[M|_]).
middle(L,M):- append(L1,L2,L),length(L1,N),length(L2,N1), N is N1+1 ,
reverse(L1,[M|_]).
Example:
?- middle([1,2,3],M).
M = 2 ;
false.
?- middle([1,2,3,4],M).
M = 2 ;
false.
In your implementation the problem is that by writing for example:
list_length([X|XTail|Y], 2)
The above does not give you as X the first element and as Y the last so I think it has some major problems...
As well pointed out by lurker you could write the above solution in one clause without using reverse/2:
middle(L, M) :- append(L1, [M|T], L), length(L1, N), length([M|T], N1),
(N1 is N + 1 ; N1 is N + 2).
Also to make the solution more relational (also see mat's comment below) you could use CLPFD library and replace is/2 with #= like:
middle(L, M) :- append(L1, [M|T], L), length(L1, N), length([M|T], N1),
(N1 #= N + 1 ; N1 #= N + 2).
Another interesting solution is to consider this predicate for splitting a list in half:
half(List, Left, Right) :-
half(List, List, Left, Right).
half(L, [], [], L).
half(L, [_], [], L).
half([H|T], [_,_|T2], [H|Left], Right) :-
half(T, T2, Left, Right).
This predicate divides an even list into two equal halves, or an odd list into two pieces where the right half has one more element than the left. It does so by reducing the original list, via the second argument, by two elements, each recursive call, while at the same time reducing the original list by one element each recursive call via the first argument. When it recurses down to the second argument being zero or one elements in length, then the first argument represents the half that's left, which is the right-hand list.
Example results for half/3 are:
| ?- half([a,b,c], L, R).
L = [a]
R = [b,c] ? a
(1 ms) no
| ?- half([a,b,c,d], L, R).
L = [a,b]
R = [c,d] ? a
no
| ?-
We can't quite use this to easily find the middle element because, in the even case, we want the last element of the left hand list. If we could bias the right-hand list by an extra element, we could then pick off the head of the right-hand half as the "middle" element. We can accomplish this using the deleteLast/2 here:
middle([X], X).
middle(List, Middle) :-
deleteLast(List, ListWithoutLast),
half(ListWithoutLast, _, [Middle|_]).
The head of the right half list of the original list, with the last element deleted, is the "middle" element. We can also simply half/3 and combine it with middle/2 since we don't really need everything half/3 does (e.g., we don't need the left-hand list, or the tail of the right hand list):
middle([X], X).
middle(List, Middle) :-
deleteLast(List, ListWithoutLast),
middle(ListWithoutLast, ListWithoutLast, Middle).
middle([M|_], [], M).
middle([M|_], [_], M).
middle([_|T], [_,_|T2], Right) :-
middle(T, T2, Right).
Another approach would be to modify half/3 to bias the splitting of the original list in half toward the right-hand half, which eliminates the need for using deleteLast/2.
modified_half(List, Left, Right) :-
modified_half(List, List, Left, Right).
modified_half(L, [_], [], L).
modified_half(L, [_,_], [], L).
modified_half([H|T], [_,_,X|T2], [H|Left], Right) :-
modified_half(T, [X|T2], Left, Right).
This will bias the right hand list to have an extra element at the "expense" of the left:
| ?- modified_half([a,b,c,d,e], L, R).
L = [a,b]
R = [c,d,e] ? a
no
| ?- modified_half([a,b,c,d,e,f], L, R).
L = [a,b]
R = [c,d,e,f] ? a
no
| ?-
Now we can see that the middle element, per the original definition, is just the head of the right hand list. We can create a new definition for middle/2 using the above. As we did before with half/3, we can ignore everything but the head in the right half, and we can eliminate the left half since we don't need it, and create a consolidated middle/2 predicate:
middle(List, Middle) :-
middle(List, List, Middle).
middle([M|_], [_,_], M).
middle([M|_], [_], M).
middle([_|T], [_,_,X|T2], Middle) :-
middle(T, [X|T2], Middle).
This reduces the original list down one element at a time (first argument) and two elements at a time (second argument) until the second argument is reduced to one or two elements. It then considers the head first argument to be the middle element:
This gives:
| ?- middle([a,b,c], M).
M = b ? ;
no
| ?- middle([a,b,c,d], M).
M = b ? ;
no
| ?- middle(L, M).
L = [M,_] ? ;
L = [M] ? ;
L = [_,M,_,_] ? ;
L = [_,M,_] ? ;
L = [_,_,M,_,_,_] ? ;
L = [_,_,M,_,_] ? ;
L = [_,_,_,M,_,_,_,_] ?
...

prolog list of how many times an element occurs in a list?

Well, I have a list, say [a,b,c,c,d], and I want to generate a list [[a,1],[b,1],[c,2],[d,1]]. But I'm having trouble with generating my list. I can count how many times the element occur but not add it into a list:
% count how much the element occurs in the list.
count([], _, 0).
count([A|Tail], A, K) :-
count(Tail, A, K1),
K is K1 + 1.
count([_|Tail], X, K) :-
count(Tail, X, K1),
K is K1 + 0.
% Give back a list with each element and how many times is occur
count_list(L, [], _).
count_list(L, [A|Tail], Out) :-
count(L, A, K),
write(K),
count_list(L, Tail, [K|Out]).
I'm trying to learn Prolog but having some difficulties... Some help will be much appreciated... Thanks in advance!
Let me first refer to a related question "How to count number of element occurrences in a list in Prolog" and to my answer in particular.
In said answer I presented a logically-pure monotone implementation of a predicate named list_counts/2, which basically does what you want. Consider the following query:
?- list_counts([a,b,c,c,d], Xs).
Xs = [a-1,b-1,c-2,d-1]. % succeeds deterministically
?- list_counts([a,b,a,d,a], Xs). % 'a' is spread over the list
Xs = [a-3,b-1,d-1]. % succeeds deterministically
Note that the implementation is monotone and gives logically sound answers even for very general queries like the following one:
?- Xs = [_,_,_,_],list_counts(Xs,[a-N,b-M]).
Xs = [a,a,a,b], N = 3, M = 1 ;
Xs = [a,a,b,a], N = 3, M = 1 ;
Xs = [a,a,b,b], N = M, M = 2 ;
Xs = [a,b,a,a], N = 3, M = 1 ;
Xs = [a,b,a,b], N = M, M = 2 ;
Xs = [a,b,b,a], N = M, M = 2 ;
Xs = [a,b,b,b], N = 1, M = 3 ;
false.
I cannot follow your logic. The easy way would be to use library(aggregate), but here is a recursive definition
count_list([], []).
count_list([H|T], R) :-
count_list(T, C),
update(H, C, R).
update(H, [], [[H,1]]).
update(H, [[H,N]|T], [[H,M]|T]) :- !, M is N+1.
update(H, [S|T], [S|U]) :- update(H, T, U).
the quirk: it build the result in reverse order. Your code, since it uses an accumulator, would give the chance to build in direct order....

Copy certain characters from a list to another on prolog

So i have this code which copies everything from a list to another one.
How should I modify it in order to copy, lets say the first two character.
$copy(L,R) :-
copy2(L,R).
copy2([X],[X]).
copy2([H|T1],[H|T2]) :-
copy2(T1,T2).
example of what i want it to be: ?- copy([a,b,c,d,e,f],X,2). --> X = [a,b]
You can copy lists just with unification:
?- [a,b,c,d,e] = List.
List = [a, b, c, d, e].
?- [a,b,c,d,e] = [V,W,X,Y,Z].
V = a,
W = b,
X = c,
Y = d,
Z = e.
?- [a,b,c,d,e] = [V,W|Rest].
V = a,
W = b,
Rest = [c, d, e].
A predicate like the one you describe, copying the first N elements of a list, can be defined thus:
first_n(List, N, Xs) :-
length(Xs, N),
append(Xs _, List).
Which works like so:
?- first_n([a,b,c,d,e], 2, X).
X = [a, b].
There are a bunch of different ways to write a similar predicate. The way I have defined first_n/3, it will fail if N is larger than the length of List (this was pointed to out by #false in the comments). One could instead write an analog of the common function take, which will return List in its entirety in the event that N is greater than List's length:
take_n(N, List, Taken) :-
( length(List, M),
N > M
->
Taken = List
;
length(Taken, N),
append(Taken, _, List)
).
This answer was corrected (several times) under the guidance of #false's helpful criticism.

How to access facts inside list

I'm trying to extract the elements of a list of pairs, but I'm only able to access the pairs. If I have a list like this:
list([p(a,b),p(c,d)]).
How can I access the elements a, b, c, d. For example, if I run:
list_s(X) :- list(L), member(X,L).
?- list_s(X).
X = p(a,b),
X = p(c,d)
I get all the pairs in the list. But I'm trying to write a rule:
listSelect(X) :- list(X), ( ... something).
?- listSelect(X).
X = a,
X = b,
X = c,
X = d
Is it possible to make a rule to do something like this?
Rubens' answer is perfectly fine, if, however, the order is important, you can also write:
list_select(X) :-
list(L),
member(p(A,B), L),
( X = A
; X = B
).
As a small bonus, you only traverse the original list once.
If you wanted to make this work on functors with an arbitrary arity, you could instead write:
list_select(X) :-
list(L),
member(F, L),
F =.. [_N|Args],
member(X, Args).
You may simply run through the possible facts of your list, as in:
list([p(a, b), p(c, d)]).
listSelect(X) :- list(L), member(p(X, _), L).
listSelect(X) :- list(L), member(p(_, X), L).
Which gives:
?- listSelect(A).
A = a ;
A = c ;
A = b ;
A = d.

Deleting all members of a list without unification in Prolog [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Prolog delete: doesn't delete all elements that unify with Element
In Prolog if you write this:
delete([(1,1),(1,2),(1,1),(3,4)],(1,_),L).
the result will be:
L = [ (1, 2), (3, 4)].
What is normal because the _ variable binds with 1 in the first element and it searches for further elements of (1,1) and deletes them.
Is there a way to prevent this unification from happening and deleting all members of the form (1,_).
In that case the result must be:
L = [ (3, 4)].
delete_pattern([], _, []).
delete_pattern([H|T], P, O) :-
( H \= P
-> O = [H|O1],
delete_pattern(T, P, O1)
; delete_pattern(T, P, O) ).
You may like to use other predicates for filtering that would result in slightly different semantics as ==/2 or =#=/2.
Here is a another version. Actually, a pure one:
list_el_deleted([], _, []).
list_el_deleted([X|Xs], X, Ys) :-
list_el_deleted(Xs, X, Ys).
list_el_deleted([X|Xs], E, [X|Ys]) :-
dif(X,E),
list_el_deleted(Xs, E, Ys).
Trying it on your query reveals the actual sources of ambiguity in your problem statement:
?- list_el_deleted([(1,1),(1,2),(1,1),(3,4)],(1,X),L).
X = 1, L = [(1,2),(3,4)]
; X = 2, L = [(1,1),(1,1),(3,4)]
; L = [(1,1),(1,2),(1,1),(3,4)], dif(X,1), dif(X,2), dif(X,1).
So it all depends on what X actually is: 1, 2, or something else. Note that the previous versions discussed here depend on the actual instantiation which makes reasoning much more difficult:
?- delete([a],X, Xs), X = c.
false.
?- X = c, delete([a],X, Xs).
X = c, Xs = [a].