6

I've wrapped my mind a lot and couldn't figure it out. Is it possible to make a script that with backtrack generates lists in this format:

[a]
[a,b]
[a,b,a]
[a,b,a,b]
...

I've made one that generates two elements at a time but my head started to hurt trying to make one that generates "a" and the next time "b" and the next "a" and so on.

Here is the script for two elements at a time:

ab([a]).
ab([b,a|T]):-ab([a|T]).
ab([a,b|T]):-ab([b|T]).
Guy Coder
  • 24,501
  • 8
  • 71
  • 136

3 Answers3

8

When describing lists, always consider using DCG notation.

This makes it very convenient to focus an the essence of what you want to describe, without so many additional variables and arguments.

For example, consider:

abs --> [a], abs_rest.

abs_rest --> [].
abs_rest --> [b], ( [] | abs ).

Sample query and answer:

?- phrase(abs, ABs).
ABs = [a] ;
ABs = [a, b] ;
ABs = [a, b, a] ;
ABs = [a, b, a, b] ;
ABs = [a, b, a, b, a] ;
ABs = [a, b, a, b, a, b] .

See for more information about this convenient formalism!

mat
  • 40,498
  • 3
  • 51
  • 78
  • Thanks! That is interesting, did not stumble upon it before and we certainly don't learn it in our course in my university, it's valuable information! Anyway I am training my recursion kills and making generating scripts and I am not sure if DCGs will be accepted on my exam so I am still searching for a solution in the traditional way :) – Dimitur Epitropov Jan 18 '17 at 19:30
  • 1
    If by "in the traditional way", you mean "in a less readable way", simply use the following query to get plain Prolog code corresponding to the DCG: `?- listing(abs//0), listing(abs_rest//0).` You can take its output, hand it to your instructor, and know that there is a better solution that you probably are not allowed to use, because why use a fitting formalism when there is a more complex way to express the same thing too? – mat Jan 18 '17 at 19:37
  • Traditional wasn't the most suitable word to describe what I need. I am trying to figure it out written only using Head, Tail and recursion properties of the lists. In such fashion: ab([a]). ab([b,a|T]):-ab([a|T]). ab([a,b|T]):-ab([b|T]). – Dimitur Epitropov Jan 18 '17 at 19:49
  • 1
    As I said: Load the DCG above, and then use `listing/1` to make your Prolog system output the lower-level code that you can use as well. Internally, your system compiles the DCG down to plain Prolog, and you can inspect the generated code with `listing/1` (if your system provides it). – mat Jan 18 '17 at 20:12
  • Thanks mat! Seems I was kind of tired already yesterday and didn't catch that the first time, everything is clear now :) – Dimitur Epitropov Jan 19 '17 at 10:31
4

I agree with @mat that one should use when possible for these type of problems.

Here is a different set of rules.

abs --> [a].
abs --> [a,b].
abs --> [a,b], abs.

?- phrase(abs, Ls).
Ls = [a] ;
Ls = [a, b] ;
Ls = [a, b, a] ;
Ls = [a, b, a, b] ;
Ls = [a, b, a, b, a] ;
Ls = [a, b, a, b, a, b] ;
Ls = [a, b, a, b, a, b, a] ;
Ls = [a, b, a, b, a, b, a, b] ;
Ls = [a, b, a, b, a, b, a, b, a] 

Interestingly those rules started from this variation

abs2 --> [].
abs2 --> [a].
abs2 --> [a,b], abs2.

?- phrase(abs2, Ls).
Ls = [] ;
Ls = [a] ;
Ls = [a, b] ;
Ls = [a, b, a] ;
Ls = [a, b, a, b] ;
Ls = [a, b, a, b, a] ;
Ls = [a, b, a, b, a, b] ;
Ls = [a, b, a, b, a, b, a] ;
Ls = [a, b, a, b, a, b, a, b] 

which is one of the exercises from Using Definite Clause Grammars in SWI-Prolog

If you prefer not to use DCG, then I agree with @mat and suggest that you use listing/1 to see the DCG in standard Prolog syntax.

listing(abs).

abs([a|A], A).
abs([a, b|A], A).
abs([a, b|A], B) :-
        abs(A, B).

listing(abs2).  

abs2(A, A).
abs2([a|A], A).
abs2([a, b|A], B) :-
        abs2(A, B).

As normal Prolog rules they can be used as such:

abs(X,[]).
X = [a] ;
X = [a, b] ;
X = [a, b, a] ;
X = [a, b, a, b] ;
X = [a, b, a, b, a] ;
X = [a, b, a, b, a, b] ;
X = [a, b, a, b, a, b, a]

abs2(X,[]).
X = [] ;
X = [a] ;
X = [a, b] ;
X = [a, b, a] ;
X = [a, b, a, b] ;
X = [a, b, a, b, a] ;
X = [a, b, a, b, a, b] 
Guy Coder
  • 24,501
  • 8
  • 71
  • 136
  • 1
    The text you link to uses outdated terminology, please use a better maintained document. – mat Jan 18 '17 at 20:58
  • @mat I take it you are referring to, `Using Definite Clause Grammars in SWI-Prolog`. I added the link for that to show that there are other similar problems, that's basically the reason for the link. – Guy Coder Jan 18 '17 at 21:05
  • Thanks mate, for further explaining this, appreciated! – Dimitur Epitropov Jan 19 '17 at 10:32
1

The predicate you wrote is too general:

?- ab([b,a]).
true

The cause is the following rule

ab([b,a|T]) :-
  ab([a|T]).

One could say that you describe an intermediate result which is no solution to your actual problem. To fix this, you could state than an ab sequence starts with a and the rest bust be a ba sequence, where ba sequences are again defined in terms of ab sequences:

ab([a]).
ab([a|Xs]) :-
    ba(Xs).

ba([b]).
ba([b|Xs]) :-
    ab(Xs).

Alternatively, you could think of abs as a state machine which produces a whenever the last element was b and vice versa. If we introduce an additional argument to trace the history, we arrive at:

abs(a,[a]).
abs(b,[b]).
abs(a,[a|Xs]) :-
    abs(b,Xs).
abs(b,[b|Xs]) :-
    abs(a,Xs).

Now we define an ab sequence as one which last prepended an a:

ab(Xs) :-
    abs(a,Xs).

Have fun :)

lambda.xy.x
  • 4,918
  • 24
  • 35