The combination of the list data structure and the ability to define recursive rules allows you to create several highly reusable algorithms. All the Logikus programs in this chapter are included in this book's CD in .txt files. You can open these files with any editor and copy and paste them into your own programs.
The member program is probably the most fundamental and most reused algorithm in logic programming. The program (from file member.txt ) is
member(X, [X Rest]); member(X, [Y Rest]) :- member(X, Rest);
Figure 13.7 shows this program in use.
Figure 13.7. The member program. The query uses the classic logic program member to extract chess champs of the late 20th century from a list.
A high-level description of the member program is that an element is a member of a list if it heads the list or if it is a member of the tail of the list. A closer analysis of the logic of the member program is as follows .
The first rule states that a variable is a member of a list if it heads the list. When the query in the example proves itself, it unifies with the first rule of the program. This results in success, with the variable in the query unifying with the first element of the list, spassky .
When the query tries to find another proof, it asks the first rule for another proof, but there is none. The query then unifies with the second rule and tries to prove it by proving its second structure, which is member(X, Rest) . This structure proves itself in exactly the same way the query structure did. It uses the first rule to find the head of Rest and the second rule to find members of the list after its head. This recurses until the list is empty, and both rules fail.
A prefix of a list is another list whose elements equal the initial elements of the first list. For example,  and [merlin] are two prefixes of
[merlin, prospero, gandalf, harry]
The following program finds all the prefixes of a list:
prefix(, List); prefix([X Rest1], [X Rest2]) :- prefix(Rest1, Rest2);
Figure 13.8 shows a query that applies this program to a list of wizards.
Figure 13.8. The prefix program. The query asks for all prefixes of a list of wizards.
The second rule of prefix declares that one list is a prefix of another if their heads are the same ( X ) and the first list's tail is a prefix of the second list's tail.
The suffix algorithm uses the same concepts as the prefix algorithm.
/* usage: suffix(SuffixList, LongerList) */ suffix(List, List); suffix(List1, [X Rest]) :- suffix(List1, Rest);
One list is a permutation of another list if it contains the same elements in any order. Figure 13.9 shows a permutation algorithm applied to a list of errands.
Figure 13.9. A permutation algorithm. The query uses the permutation algorithm to find possible sequences for completing a list of errands.
The file permutation.txt includes the select algorithm, whose rules are
select(X, [X Rest], Rest); select(X, [Y Rest1], [Y Rest2]) :- select(X, Rest1, Rest2);
These rules separate a list into two parts : an element extracted from the list, and the remainder of the list. The select algorithm works by declaring that X is a selectable element of a list if X heads the list or if X is selectable from the tail of the list.
Here is the permutation algorithm:
permutation(InList, [H OutRest]) :- select(H, InList, InOther), permutation(InOther, OutRest); permutation(, );
This algorithm declares that an output list is a permutation of an input list if the output is an element of the input, followed by a permutation of the remainder of the input. This rule recurses, finding permutations of an ever-smaller list, until finally it asks whether an empty list is a permutation of itself, which the second rule supports.