Reputation: 69
I'd like to be able to generate all possible lists of elements, with an ‘and’ between the last two elements (as in normal language). There should be no duplicate elements in any list. So ‘three, two, and, four’ is a correct list, as is ‘two and one’, or ‘five, three, four, one, and, two’. I am not looking for single-element lists.
I’ve written the following program. When I query Prolog about a specific list, it will tell me correctly if it is a valid list. However, when I query list(X), it only generates lists with two elements.
Could someone suggest a way to make it generate all possible lists, using the given elements? Thank you!
I have facts such as
element([one]).
element([two]).
element([three]).
element([four]), etc. etc.
My predicates to generate a list L are:
list(L):-
element(E1),
element(E2),
E1 \= E2,
append(E1, [and], X),
append(X, E2, L).
%recursive case
list(L):-
element(E),
append(E, [','], X),
append(X, Y, L),
list(Y),
not_in(E, Y).
not_in([X], [Y]):- X \= Y.
not_in([X], [H|T]):-
[X] \= [H],
not_in([X], T).
Upvotes: 0
Views: 1163
Reputation: 3407
Your code loops because list(L)
is defined in terms of list(Y)
(recursive definition). element(E)
picks the first occurrence, i.e. [one]
, and then prepends this to a new list Y
, ad infinitum. You are checking that E
does not reoccur in Y
in the last line, but the loop appears before that.
list(L):-
element(E),
append(E, [','], X),
append(X, Y, L),
list(Y),
not_in(E, Y).
Since using append/3
several times can get messy, I am using DCGs here. The trick is to keep a list around of already used elements so that we can check for duplicate occurrences up front. This avoid the looping behavior that occurs when trying to check for this afterwards.
list -->
list([]).
list(T) -->
element(T, H),
[and],
element([H|T], _).
list(T) -->
element(T, H),
[','],
list([H|T]).
element(L, X) -->
cardinal(X),
{\+ memberchk(X, L)}.
cardinal(1) --> [one].
cardinal(2) --> [two].
cardinal(3) --> [three].
cardinal(4) --> [four].
Example of use:
?- phrase(list, List).
List = [one, and, two] ;
...
List = [one, ',', two, ',', three, and, four] ;
...
List = [four, ',', three, ',', two, and, one] ;
false
Upvotes: 1