How to make Prolog able to generate all possible s

2019-09-01 06:34发布

问题:

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).

回答1:

Looping bug

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).

Correct implementation

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


标签: prolog