Find combinations/sub-lists of Prolog list elements
I'm trying to write a function that will produce the combinations of list elements formed by removing 1 or 2 elements, including splitting the list.
For Example:
[b,b,b]
could become
[b,b]
[b]
[[b],[b]]
I can generate the firs开发者_高级运维t two using the following predicates:
permute([_|T],T).
permute([_|[_|T]],T).
But the third is proving more difficult. I can use append
to split the list into two parts, but I don't know how to combine that with what I already have.
I'm using SWI-Prolog.
(Although tagged as Homework, this is one part of a larger assignment, and is where I am stuck at the moment)
Taking the game of Kayles as our application suggests an economical representation of state would be an ordered list of positive integers, with each number representing a consecutive string of bottles.
I say ordered because the state of a game is equivalent under any permutation of those values.
Possible moves would therefore consist of choosing an entry and reducing it by one or two into zero, one or two smaller entries. These smaller entries, if any, should then be inserted in the appropriate places in the new "tail" of the game state list.
A building block for the larger representation of game moves is to decide how many ways a consecutive string of X bottles can be reduced. So we can start with that requirement:
bowls(X,[ ]) :-
( 0 is X - 1 ; 0 is X-2 ).
bowls(X,[W]) :-
( W is X - 1 ; W is X-2 ),
W > 0.
bowls(X,[Y,Z]) :-
( W is X - 1 ; W is X-2 ),
W > 1,
bowlsplits(W,Y,Z).
It is left to the reader to implement bowlsplits/3 in such a way that it produces all possibilities that Y + Z = W
with Y >= Z > 0
.
Note that with such a representation we should avoid duplicate outcomes by skipping over any entry that is equal to the following entry in the list, as two equal entries give rise to the same possible outcomes.
kayles([X],R) :- bowls(X,R).
kayles([X,Y|T],R) :-
X > Y,
bowls(X,L),
inSort(L,[Y|T],R).
kayles([X,X|T],[X|L]) :-
kayles([X|T],L).
It is also left to the reader to implement inSort/3 along the lines of an insertion sort that merges the ordered lists in its first two arguments into an ordered list for binding the third argument.
Note that kayles/2 is tail-recursive but non-deterministic.
The predicate
permute([_|T],T).
permute([_|[_|T]],T).
would be more aptly named tail_or_tail_of_tail
. Have you noticed that it can only remove the first or the first two elements? If you want to take out any two elements, use something like
take_out_one_or_two(L, R) :- select(_, L, R).
take_out_one_or_two(L, R) :- select(_, L, L1), select(_, L1, R).
You're right that splitting can be done with append/3
. Your (rather strange) requirement is met by
strange_predicate(L, R) :- take_out_one_or_two(L, R).
strange_predicate(L, [X,Y]) :- take_out_one_or_two(L, L1), append(X, Y, L1).
If you don't want empty lists in your results, replace the second clause with
strange_predicate(L, [X,Y]) :-
take_out_one_or_two(L, L1),
X = [_|_],
Y = [_|_],
append(X, Y, L1).
(I really can't think of a name for this predicate without a use case.)
精彩评论