Está en la página 1de 6

List Processing in Prolog The notation [H|T] represents a list

with H matching the head of the list


Prolog has a notation similar to “cons and T matching the rest of the list.
cells” of Lisp and Scheme. Thus [1,2,3] ≡ [1| [2,3]] ≡
The “.” functor (predicate name) acts [1,2| [3]] ≡ [1,2,3| [] ]
like cons. As in ML, “_” (underscore) can be
Hence .(a,b) in Prolog is essentially used as a wildcard or “don’t care”
the same as (a . b) in Scheme. symbol in matches.
Lists in Prolog are formed much the Given the fact
same way as in Scheme and ML: p([1,2,3,4]).
[] is the empty list
The query
[1,2,3] is an abbreviation for | ?- p([X|Y]).
.(1, .(2, .(3,[])))
answers
just as X = 1,
(1,2,3) in Scheme is an abbreviation Y = [2,3,4]
for
(cons 1 (cons 2 (cons 3 () )))

© ©
CS 538 Spring 2006 412 CS 538 Spring 2006 413

The query List Operations in Prolog


p([_,_,X|Y]).
List operations are defined using rules
answers and facts. The definitions are similar
X = 3, to those used in Scheme or ML, but
Y = [4] they are non-procedural.
That is, you don’t given an execution
order. Instead, you give recursive rules
and non-recursive “base cases” that
characterize the operation you are
defining.
Consider append:
append([],L,L).
append([H|T1],L2,[H|T3]) :-
append(T1,L2,T3).
The first fact says that an empty list
(argument 1) appended to any list L
(argument 2) gives L (argument 3) as
its answer.

© ©
CS 538 Spring 2006 414 CS 538 Spring 2006 415
The rule in line 2 says that if you take Inverting Inputs and Outputs
a list that begins with H and has T1 as
the rest of the list and append it to a In Prolog the division between
list L then the resulting appended list “inputs” and “outputs” is
will begin with H. intentionally vague. We can exploit
this. It is often possible to “invert” a
Moreover, the rest of the resulting
query and ask what inputs would
list, T3, is the result of appending T1
compute a given output. Few other
(the rest of the first list) with L2 (the
languages allow this level of
second input list).
flexibility.
The query
Consider the query
| ?- append([1],[2,3],[1,2,3]).
answers append([1],X,[1,2,3]).

Yes
This asks Prolog to find a list X such
that if we append [1] to X we will
because with H=1, T1=[], L2 =[2,3] get [1,2,3].
and T3=[2,3] it must be the case
that append([],[2,3],[2,3]) is Prolog answers
true and fact (1) says that this is so. X = [2,3]

© ©
CS 538 Spring 2006 416 CS 538 Spring 2006 417

How does it choose this answer? The Member Relation


First Prolog tries to match the query
A common predicate when
against fact (1) or rule (2).
manipulating lists is a membership
Fact (1) doesn’t match (the first test—is a given value a member of a
arguments differ) so we match rule list?
(2).
An “obvious” definition is a recursive
This gives us H=1, T1=[], L2=X and one similar to what we might
T3 = [2,3]. program in Scheme or ML:
We next have to solve the body of member(X,[X|_]).
rule (2) which is member(X,[_|Y]):- member(X,Y).
append([],L2,[2,3]). This definition states that the first
argument, X, is a member of the
Fact (1) matches this, and tells us
second argument (a list) if X matches
that L2=[2,3]=X, and that’s our
the head of the list or if X is
answer!
(recursively) a member of the rest of
the list.

© ©
CS 538 Spring 2006 418 CS 538 Spring 2006 419
Note that we don’t have to “tell” If we want to know if any member of
Prolog that X can’t be a member of an a list L satisfies a predicate p, we can
empty list—if we don’t tell Prolog simply write:
that something is true, it member(X,L),p(X).
automatically assumes that it must be
false. There is no explicit iteration or
searching. We simply ask Prolog to
Thus saying nothing about find an X such that member(X,L) is
membership in an empty list is the true (X is in L) and p(X) is true.
same as saying that membership in an Backtracking will find the “right”
empty list is impossible. value for X (if any such X exists).
Since inputs and outputs in a relation This is sometimes called the “guess
are blurred, we can use member in an and verify” technique.
unexpected way—to iterate through a
list of values. Thus we can query
member(X,[3,-3,0,10,-10]),
(X > 0).
This asks for an X in the list
[3,-3,0,10,-10] which is greater
than 0.

© ©
CS 538 Spring 2006 420 CS 538 Spring 2006 421

Prolog answers Prolog solves a query involving


X = 3; member by partitioning the list L in
all possible ways, and checking to see
X = 10 ;
if X ever is the head of the second
Note too that our “obvious” list. Thus for member(X,[1,2,3]), it
definition of member is not the only tries the partition [] and [1,2,3]
one possible. (exposing 1 as a possible X), then [1]
An alternative definition (which is far and [2,3] (exposing 2) and finally
less obvious) is [1,2] and [3] (exposing 3).
member(X,L) :-
append(_,[X|_],L).
This definition says X is a member of
L if I can take some list (whose value
I don’t care about) and append it to a
list that begins with X (and which
ends with values I don’t care about)
and get a list equal to L.
Said more clearly, X is a member of L
if X is anywhere in the “middle” of L.

© ©
CS 538 Spring 2006 422 CS 538 Spring 2006 423
Sorting Algorithms We can implement this concept of a
sort directly in Prolog. We
Sorting algorithms are good examples (a) permute an input list
of Prolog’s definitional capabilities. In (b) check if it is in sorted order
a Prolog definition the “logic” of a
sorting algorithm is apparent, (c) repeat (a) & (b) until a sorting is
stripped of the cumbersome details of found.
data structures and control structures
that dominate algorithms in other
programming languages.
Consider the simplest possible sort
imaginable, which we’ll call the
“naive sort.”
At the simplest level a sorting of a list
L requires just two things:
• The sorting is a permutation (a
reordering) of the values in L.
• The values are “in order” (ascending
or descending).

© ©
CS 538 Spring 2006 424 CS 538 Spring 2006 425

Permutations The first definition,


perm([],[]).
Let’s first look at how permutations
is trivial. An empty list may only be
are defined in Prolog. In most
permuted into another empty list.
languages generating permutations is
non-trivial—you need data structures The second definition is rather more
to store the permutations you are complex:
generating and control structures to perm(L,[H|T]) :-
visit all permutations in some order. append(V,[H|U],L),
append(V,U,W), perm(W,T).
In Prolog, permutations are defined
quite concisely, though with a bit of This rule says a list L may be
subtlety: permuted in to a list that begins with
H and ends with list T if:
perm(X,Y) will be true if list Y is a
permutation of list X. (1) L may be partitioned into two
lists, V and [H|U]. (That is, H is
Only two definitions are needed: somewhere in the “middle” of L).
perm([],[]).
(2) Lists V and U (all of L except H)
perm(L,[H|T]) :- may be appended into list W.
append(V,[H|U],L),
append(V,U,W), perm(W,T). (3) List W may be permuted into T.

© ©
CS 538 Spring 2006 426 CS 538 Spring 2006 427
Let’s see perm in action: One solution to this goal is
| ?- perm([1,2,3],X). V = [], H = 1, U = [2,3]
X = [1,2,3] ;
We next solve append(V,U,W) which
X = [1,3,2] ; simplifies to append([],[2,3],W).
X = [2,1,3] ;
The only solution for this is W=[2,3].
X = [2,3,1] ;
X = [3,1,2] ; Finally, we solve perm(W,T), which
X = [3,2,1] ; simplifies to perm([2,3],T).
no One solution to this is T=[2,3].
We’ll trace how the first few answers This gives us our first solution:
are computed. Note though that all [H|T]=[1,2,3].
permutations are generated, and with To get our next solution we backtrack.
no apparent data structures or Where is the most recent place we
control structures. made a choice of how to solve a goal?
We start with L=[1,2,3] and It was at perm([2,3],T). We chose
X=[H|T].
T=[2,3], but T=[3,2] is another
We first solve append(V,[H|U],L), solution. Using this solution, we get
which simplifies to out next answer [H|T]=[1,3,2].
append(V,[H|U],[1,2,3]).

© ©
CS 538 Spring 2006 428 CS 538 Spring 2006 429

Let’s try one more. We backtrack A Permutation Sort


again. No more solutions are possible
for perm([2,3],T), so we backtrack Now that we know how to generate
to an earlier choice point. permutations, the definition of a
At append(V,[H|U],[1,2,3]) permutation sort is almost trivial.
another solution is We define an inOrder relation that
V=[1], H = 2, U = [3]
characterizes our notion of when a
list is properly sorted:
Using this binding, we solve inOrder([]).
append(V,U,W) which simplifies to
inOrder([_]).
append([1],[3],W). The solution to
inOrder([A,B|T]) :-
this must be W=[1,3]. A =< B, inOrder([B|T]).
We then solve perm(W,T) which These definitions state that a null list,
simplifies to perm([1,3],T). One and a list with only one element are
solution to this is T=[1,3]. This always in sorted order. Longer lists
makes our third solution for [H|T] = are in order if the first two elements
[2,1,3]. are in proper order. (A=<B) checks
You can check out the other bindings this and then the rest of the list,
that lead to the last three solutions. excluding the first element, is
checked.

© ©
CS 538 Spring 2006 430 CS 538 Spring 2006 431
Now our naive permutation sort is Though this sort works, it is
only one line long: hopelessly inefficient—it repeatedly
naiveSort(L1,L2) :- “shuffles” the input until it happens
perm(L1,L2), inOrder(L2). to find an ordering that is sorted. The
And the definition works too! process is largely undirected. We
| ?- don’t “aim” toward a correct ordering,
naiveSort([1,2,3],[3,2,1]). but just search until we get lucky.
no
?- naiveSort([3,2,1],L).
L = [1,2,3] ;
no
| ?-
naiveSort([7,3,88,2,1,6,77,
-23,5],L).
L = [-23,1,2,3,5,6,7,77,88]

© ©
CS 538 Spring 2006 432 CS 538 Spring 2006 433

También podría gustarte