Nobody talked about difference lists yet.
Difference lists
Difference lists are denoted L-E
, which is just a convenient notation for a couple made of a list L
whose last cons-cell has E
for its tail:
L = [ V1, ..., Vn | E]
The empty difference list is E-E
, with E
a variable. You unify E
whenever you want to refine the list.
For example, if you want to add an element X
, you can unify as follows:
E = [X|F]
And then, L-F
is the new list. Likewise, you can append lists in constant time. If you unify F
with a "normal" list, in particular []
, you close your open-ended list. During all operations, you retain a reference to the whole list through L
. Of course, you can still add elements in front of L
with the ususal [W1, ..., Wm |L]-E
notation.
Whether or not you need difference lists is another question. They are intereseting if adding an element at the end is effectively a common operation for you and if you are manipulating large lists.
Definite clause grammars
DCG are a convenient way of writing grammar rules in Prolog. They are typically implemented as reader macros, translating -->
forms into actual predicates. Since the purpose of grammars is to build structures during parsing (a.k.a. productions), the translation to actual predicates involves difference lists. So, even though both concepts are theoretically unrelated, difference lists are generally the building material for DCGs.
The example on wikipedia starts with:
sentence --> noun_phrase, verb_phrase.
... which gets translated as:
sentence(S1,S3) :- noun_phrase(S1,S2), verb_phrase(S2,S3).
A lot of "plumbing" is provided by the syntax (a little like monads). The object being "built" by sentence/2
is S1, which is built from different parts joined together by the predicates. S1 is passed down to noun_phrase
, which builds/extends it as necessary and "returns" S2, which can be seen as "whatever extends S1". This value is passed to verb_phrase
which updates it and gives S3, a.k.a. whatever extends S2. S3 is an argument of sentence
, because it is also "whatever extends S1", given the rule we have. But, this is Prolog, so S1, S2 and S3 are not necessarly inputs or outputs, they are unified during the whole process, during which backtracking takes place too (you can parse ambiguous grammars). They are eventually unified with lists.
Difference lists come to play when we encounter lists on the right-hand side of the arrow:
det --> [the].
The above rule is translated as:
det([the|X], X).
That means that det/2
unifies its first argument with an open-ended list which tail is X
; other rules will unify X
. Generally, you find epsilon rules which are associated with []
.
All the above is done with macros, and a typical error is to try to call an auxiliary predicate on your data, which fails because the transformation add two arguments (a call to helper(X)
is in fact a call to helper(X,V,W)
). You must enclose actual bodies between braces { ... }
to avoid treating prediates as rules.