List manipulation

Lists may be used to store and manipulate sequences of items. Prolog offers a special syntax to deal with lists. $=P \prologindex{list manipulation} \prologindex{\emptylist} \prologindex{\prologlist} \prologindex{\compoundlist} Examples of terms that denote lists are [] & -- denoting the empty list, \\ [a,b,c] & -- denoting the list with elements a, b and c, and \\ [H|T] & -- denoting the list T with element H put in front. \end{tabular} Internally, non-empty lists may be thought to be represented as terms constructed with the function-symbol cons. We use [] to represent the empty list. To put an element a in front of the empty list we write cons(a,[]). The list [a,b,c] is represented by the term cons(a,cons(b,cons(c,[]))). Lists in Prolog are always ended by an empty list indicating an invisible last element. The special notation [H|T] closely corresponds to the use of the constructor cons. The one-element list [a] may be written as [a|[]]. And similarly, the list [a,b,c] may be written as [a|[b,c]] or [a|[b|[c]]] or [a|[b|[c|[]]]]. The notation [H|T], which stands for a non-empty list with element H in front, is very convenient for performing operations on a list as illustrated by the clauses below. $= The predicate member tests whether an item is an element of a given list. \prologindex{\it member} \pprog{member}{ .ds member.pl } The first clause states that X is a member of the list [X|_] if X is identical to the first clause of the list, regardless of the contents of the rest of the list. The second clause proceeds by checking whether X is a member of the list excluding the first element. \prologindex{\it append} The predicate append concatenates two lists. \pprog{append}{ .ds append.pl } Again, two cases are distinguished. When the first list is empty then the result is the second list. Otherwise, the front element of the first list is made the front element of the list that results from appending the second list to the rest of the first list. Recursive definitions as exemplified by the clauses for member and append are typical of clauses dealing with lists or other recursive data-structures. The correctness of these definitions can be easily verified by looking at the declarative meaning of the clauses, taking account of the possible cases that may arise. The definitions of both member and append may be read in a functional style. However, the predicate member can be used not only to check whether an element occurs in a list, but also to bind a variable successively to all the elements in a list. In a similar way, the predicate append may be used, perhaps somewhat surprisingly to generate all the sublists of a given list. The predicate sublist checks whether a list S is a sublist of the list L. \pprog{sublist}{ .ds sublist.pl } A list is a sublist of a given list when it coincides with a part of the given list, in other words when appending a list in front of the sublist and a list to the rear results in the given list. The lists appended in the front and to the rear of the sublist may be empty, and so may be the sublist itself. In this way all the possible sublists of a list may be generated, by backtracking over the possible combinations. The predicates delete and insert may be used to delete or insert an element. \pprog{delete}{ .ds insdel.pl } The definition of insert reflects the fact that insertion is exactly the opposite of deletion. The predicate permutation uses insert in a straightforward way. \pprog{permute}{ .ds permutation.pl } Declaratively, the list R\ is a permutation of the list [H|T] if R equals [H|L] where L\ is a permutation of T. The permutation of an empty list is just the empty list. Admittedly, these definitions may be hard to grasp at first, even if the structure is on the surface quite simple. The reader not familiar with this kind of definitions is advised to get hold of a Prolog system and to try to run these programs.