Split a list on an index in Prolog - prolog

I am writing a split predicate with 4 parameters. The first parameter (Index) is the index at which the complete list should be split. The second parameter (CompleteList) is the list that the predicate should split on a given index. Parameter 3 and 4 are output parameters (here FirstHalf and SecondHalf).
split(_,[],[],[]).
split(0,CompleteList,FirstHalfList,CompleteList).
split(Index,[CompleteListHead|CompleteListTail],FirstHalfList,SecondHalfList) :-
append(FirstHalfList,[CompleteListHead],NewFirstHalfList),
NewIndex is Index-1,
split(NewIndex,CompleteListTail,NewFirstHalfList,SecondHalfList).
An example of the desired output of a query would be
split(2,[5,4,3,2,1],X,Y).
X=[5,4],
Y=[3,2,1]
I understand how the second half of the list can be returned, yet I have trouble returning the first half. The current output of the program for the example query is
split(2,[5,4,3,2,1],X,Y).
X=[],
Y=[3,2,1]
Is it possible to return the FirstHalfList (instead of the NewFirstHalfList, which backtracks to an empty list) or is the current code a wrong approach towards writing Prolog predicates ?
Edit: thank you all for the responses, they have given me a better insight.

There are many ways to do this.
Here is a compressed way:
split(Index,List,Left,Right) :-
length(Left,Index), % Actually CREATES a list of fresh variables if "Left" is unbound
append(Left,Right,List). % Demand that Left + Right = List.
Then:
?- split(2,[5,4,3,2,1],X,Y).
X = [5, 4],
Y = [3, 2, 1].
?- split(2,[],X,Y).
false.
?- split(0,[],X,Y).
X = Y, Y = [].
It can even automagically work "in reverse":
?- split(L,[5,4,3,2,1],[5,4],Y).
L = 2,
Y = [3, 2, 1].
Think about why this works!
Debug printout helps here:
split(Index,List,Left,Right) :-
debugme(Index,List,Left,Right),
length(Left,Index),
debugme(Index,List,Left,Right),
append(Left,Right,List),
debugme(Index,List,Left,Right).
debugme(Index,List,Left,Right) :-
format("Index: ~q, List: ~q, Left: ~q, Right: ~q\n",[Index,List,Left,Right]).
Then:
?- split(L,[5,4,3,2,1],[5,4],Y).
Index: _6640, List: [5,4,3,2,1], Left: [5,4], Right: _6646
Index: 2, List: [5,4,3,2,1], Left: [5,4], Right: _6646
Index: 2, List: [5,4,3,2,1], Left: [5,4], Right: [3,2,1]
L = 2,
Y = [3, 2, 1].
Notice the way in which the set of variables appearing in the clause get progressively filled with information.

The split predicate recursively reduces its second argument.
The first clause handles the terminal case (when Index > length of given list)
The second clause handles the other terminal case when Index is reduced to 0.
split(_,[],[],[]).
split(0,L,[],L).
split(Index,[CompleteListHead|CompleteListTail],[CompleteListHead|FirstHalfTail],SecondHalfList) :-
Index >= 0,
NewIndex is Index-1,
split(NewIndex,CompleteListTail,FirstHalfTail,SecondHalfList).
?- split(2,[5,4,3,2,1],X,Y).
X = [5, 4],
Y = [3, 2, 1] ;
?- split(8,[5,4,3,2,1],X,Y).
X = [5, 4, 3, 2, 1],
Y = [] ;
?- split(4,[5,4,3,2,1],X,Y).
X = [5, 4, 3, 2],
Y = [1] ;

%! split(?INDEX0,?Xs0,?Ys,?Zs)
%
% `Xs0` is split at `INDEX0` into `Ys` and `Zs` .
split(_,[],[],[]) .
split(INDEX,Xs0,Ys,Zs)
:-
lists:length(Ys,INDEX) ,
lists:append(Ys,Zs,Xs0)
.
/*
?- split(2,[5,4,3,2,1],Ys,Zs) .
Ys = [5,4] ,
Zs = [3,2,1] .
*/
/*
?- split(INDEX,Xs0,[5,4],[3,2,1]) .
INDEX = 2 ,
Xs0 = [5,4,3,2,1] .
*/
/*
?- split(INDEX,Xs0,Ys,Zs) .
Xs0 = Ys = Zs = [] ? ;
INDEX = 0 ,
Xs0 = Zs ,
Ys = [] ? ;
INDEX = 1 ,
Xs0 = [A|Zs] ,
Ys = [A] ? ;
INDEX = 2 ,
Xs0 = [A,B|Zs] ,
Ys = [A,B] ? ;
INDEX = 3 ,
Xs0 = [A,B,C|Zs] ,
Ys = [A,B,C] ? ;
INDEX = 4 ,
Xs0 = [A,B,C,D|Zs] ,
Ys = [A,B,C,D] ? %etcetera.
*/

Related

Finding consecutive sublists of a list

I want to write a predicate split/2 that generates all consecutive lists found inside another list.
Example: split([1,2,3,4],X) should return
X = [4], X = [2,3],X = [1,2], X = [1,2,3] etc.
So far I only have a predicate that returns all possible sublists of a list:
sublist([],[]).
sublist([H|T], [H|R]) :-
sublist(T,R).
sublist([_|T], R) :-
sublist(T,R).
However, with the query from the example this predicate includes unwanted answers like X = [2,4] and X = [1,3] that aren't found consecutively in [1,2,3,4].
Usually a problem is easier if you split it in subproblems. We can first construct a predicate that will construct all suffixes for a given list.
We can construct such predicate as follows:
suffix(_, []).
suffix([H|T], [H|T2]) :-
suffix(T, T2).
So for each point in the list, we can decide to stop (with the empty list), or emit the next item. For the given sample list, we thus get:
?- suffix([1,2,3,4],X).
X = [] ;
X = [1] ;
X = [1, 2] ;
X = [1, 2, 3] ;
X = [1, 2, 3, 4].
Now we only need to decide when we start the suffix. For each item in the list, we can decide to start at that point, and enumerate over all suffixes that we then append to that item:
split([H|T], [H|S]) :-
suffix(T, S).
split([_|T], S) :-
split(T, S).
For example:
?- split([1,2,3,4],X).
X = [1] ;
X = [1, 2] ;
X = [1, 2, 3] ;
X = [1, 2, 3, 4] ;
X = [2] ;
X = [2, 3] ;
X = [2, 3, 4] ;
X = [3] ;
X = [3, 4] ;
X = [4] ;
false.
The nice thing is that we got a second predicate "for free": we can also obtain all suffixes for a list.
We might want to include the empty list as well. I leave this as an exercise.

How to generate every [X,Y] possible for a given list?

I'm trying to generate every combination possible given a list. I want every [X,Y] combination possible.
Here's an example :
?- arguments(A,[1,2,3]).
A = [1,2] ; A = [1,3] ; A = [2,1] ; A = [2,3] ; A = [3,1] ;
A = [3,2]
I have tried multiple things, but I've yet to find a working one.
I am pretty sure the idea is to use prolog's ability to try every possibility as such :
element(X,[X|_],1).
element(X,[_|Q],N) :- element(X,Q,NewN), N is NewN + 1.
This predicate can return the element at the position N, or return the position of the element X, or generate every possibility. Exemple :
?- element(X,[a,b,c],N).
N = 1
X = a
N = 2
X = b
N = 3
X = c
Thanks for the help.
Edit following gusbro answer :
I can't use already existing predicates, it's part of a course.
Reading your answer, I came up with this :
remove_element_x(X, [X|Q], Q).
remove_element_x(X, [T|Q], [T|Res]) :- remove_element_x(X,Q,Res).
arguments([X,Y],L) :-
element(X,L,_),
remove_element_x(X,L,L2),
element(Y,L2,_).
remove_element_x/3 remove the element x from the list and returns the new list.
But the backtracking is not working :
?- arguments(A,[1,2,3]).
A = [1,2] ?
yes
You can use select/3 to select an element from a list (and get the remaining list), then do it again to select another element from the remaining list).
i.e.:
arguments([A,B], L):-
select(A, L, L1),
select(B, L1,_).
Test case:
?- arguments(A,[1,2,3]).
A = [1, 2] ;
A = [1, 3] ;
A = [2, 1] ;
A = [2, 3] ;
A = [3, 1] ;
A = [3, 2]

How can I modify the following Prolog program

I have written the following program to compute all pairs of members in a list. Here is my code:
select_pair(X, Y, [X|[Y|T]], T).
select_pair(X, Y, [Head|[X|[Y|T]]], [Head|Rest]) :- select_pair(X, Y, T, Rest).
I am supposed to call my code with this 3-member list only:
select pair(X, Y, [1,2,3], Zs).
But this doesn't generate all possible combinations. It only generates
X = 1, Y = 2, Zs = [3]
and it is supposed to generate this, but it does not:
X = 1, Y = 2, Zs = [3] ;
X = 1, Y = 3, Zs = [2] ;
X = 2, Y = 1, Zs = [3] ;
X = 2, Y = 3, Zs = [1] ;
X = 3, Y = 1, Zs = [2] ;
X = 3, Y = 2, Zs = [3]
So, how can I modify this code to generate all possible pairs of members of the list [1, 2, 3]?
I'd use procedure select/3:
select_pair(X, Y, List, Rest):-
select(X, List, MList),
select(Y, MList, Rest).
The first select would remove the first element from List into X and put the rest of the list in MList.
Then the second select would get you the second element and the Rest.

Prolog - extract numbers from nested list

I trying to write a function getNumbers(List,Result) such that List is a list which his elements can be integer or list of lists , for example -
List = [1,[1,2,[3],[4]],2,[4,5]]
List = [1,[1,1,[1],[1]],1,[1,1,[[[[[1]]]]]]]
List = [[4,[[]],2],[[1],[],[1]]]
etc..
And the output should be all the numbers stored in that List , for example -
?- getNumbers([1,[1,2,[3],[4]],2,[4,5]],R).
R = [1,2,3,4,5].
?- getNumbers([1,[1,1,[1],[1]],1,[1,1,[[[[[1]]]]]]],R).
R = [1].
?- getNumbers([],R).
R = [].
?- getNumbers([[4,[[]],2],[[1],[],[1]]],R).
R = [1,2,4].
So far I tried the follow code -
getNumbers([],Result) :- Result=[],!.
getNumbers([H|Rest],Result) :- getNumbers(Rest,NewResultRest),
( atomic(H) ->
Result = [H|NewResultRest]
; getNumbers(H,NewResultHead),Result = [NewResultHead|NewResultRest] ).
But it gives wrong result , like -
getNumbers([[2],5,7,[3,6,5]],Result).
Result = [[2], 5, 7, [3, 6, 5]].
It seems that the function doesn't exludes 2 from [2] or any other numbers stored in nested list.
How could I fix my implementation ?
you need to append nested lists:
getNumbers([],Result) :- Result=[],!.
getNumbers([H|Rest],Result) :-
getNumbers(Rest,NewResultRest),
( atomic(H)
-> Result = [H|NewResultRest]
; getNumbers(H,NewResultHead),
append(NewResultHead, NewResultRest, Result) % only this change
).
note that [] is atomic: thus
?- getNumbers([[4,[[]],2],[[1],[],[1]]],R).
R = [4, [], 2, 1, [], 1].
from your description, you should use number/1 to test element' type. After the change
?- getNumbers([[4,[[]],2],[[1],[],[1]]],R).
R = [4, 2, 1, 1].

Predicate to match list (all possibilities) with no duplicate integers, have length N, and be within a domain of 1 to N

This is for GNU-Prolog
I'm having trouble getting a certain predicate to work. Its functionality is that it matches a list of integers
that have a domain of 1 to N with no duplicates and length N. Basically what I want to do is have this as inputs and outputs:
| ?- row_valid(X, 3).
X = [1, 2, 3] ? ;
X = [1, 3, 2] ? ;
X = [2, 1, 3] ? ;
X = [2, 3, 1] ? ;
X = [3, 1, 2] ? ;
X = [3, 2, 1] ? ;
no
| ?- row_valid(X, 2).
X = [1, 2] ? ;
X = [2, 1] ? ;
no
| ?- row_valid(X, 1).
X = [1] ? ;
no
But right now, this is what is happening:
| ?- row_valid(X, 3).
X = [] ? ;
no
This is probably happening because of the row_valid([], _). predicate I have in the code. However, I can verify that the predicate matches correctly since:
| ?- row_valid([1,2,3], 3).
true ?
yes
Here are the predicates defined. Do you have any suggestions on how I could get this to work the way I want? Thanks for your time.
% row_valid/2: matches if list of integers has domain of 1 to N and is not duplicated
% 1 - list of integers
% 2 - N
row_valid([], _).
row_valid(Row, N) :-
length(Row, N), % length
no_duplicates_within_domain(Row, 1, N),
row_valid(RestRow, N).
% no_duplicates/1: matches if list doesn't have repeat elements
% 1 - list
no_duplicates([]). % for empty list always true
no_duplicates([Element | RestElements]) :-
\+ member(Element, RestElements), % this element cannot be repeated in the list
no_duplicates(RestElements).
% within_domain/3 : matches if list integers are within a domain
% 1 - list
% 2 - min
% 3 - max
within_domain(Integers, Min, Max) :-
max_list(Integers, Max),
min_list(Integers, Min).
% no_duplicates_within_domain/3: matches if list integers are within a domain and isn't repeated
% 1 - list
% 2 - min
% 3 - max
no_duplicates_within_domain(Integers, Min, Max) :-
no_duplicates(Integers),
within_domain(Integers, Min, Max).
How about the following?
row_valid(Xs,N) :-
length(Xs,N),
fd_domain(Xs,1,N),
fd_all_different(Xs),
fd_labeling(Xs).
Running it with GNU Prolog 1.4.4:
?- row_valid(Xs,N).
N = 0
Xs = [] ? ;
N = 1
Xs = [1] ? ;
N = 2
Xs = [1,2] ? ;
N = 2
Xs = [2,1] ? ;
N = 3
Xs = [1,2,3] ? ;
N = 3
Xs = [1,3,2] ? ;
N = 3
Xs = [2,1,3] ? ;
N = 3
Xs = [2,3,1] ? ;
N = 3
Xs = [3,1,2] ? ;
N = 3
Xs = [3,2,1] ? ;
N = 4
Xs = [1,2,3,4] ? % ...and so on...
Here is a simple piece of code that does this in SWI-Prolog. I don't know if GNU-Prolog provides between/3 and permutation/2, so maybe it doesn't directly answer your question, but maybe it can still help you further.
row_valid(List, N) :-
findall(X, between(1, N, X), Xs),
permutation(Xs, List).
Usage examples:
?- row_valid(List, 0).
List = [].
?- row_valid(List, 1).
List = [1] ;
false.
?- row_valid(List, 2).
List = [1, 2] ;
List = [2, 1] ;
false.
?- row_valid(List, 3).
List = [1, 2, 3] ;
List = [2, 1, 3] ;
List = [2, 3, 1] ;
List = [1, 3, 2] ;
List = [3, 1, 2] ;
List = [3, 2, 1] ;
false.

Resources