Article View: comp.lang.prolog
Article #4120Re: A Challenge
From: Lee Naish
Date: Thu, 21 Dec 1989 06:36
Date: Thu, 21 Dec 1989 06:36
115 lines
4909 bytes
4909 bytes
Sorry if some people get this twice - I tried to post it before but it
didn't seem to get anywhere. Sorry also if you get a garbled copy of
another recent article of mine - a slip of the mouse, and I couldn't
cancel the article because it was posted by 'news', not by me!
In article <11500022@hpldoda.UUCP> patch@hpldoda.UUCP (Pat Chkoreff) writes:
>
>Write a predicate good/1 which succeeds if and only if its argument is a
>list of lists, where all of the lists have the same length.
>
>All of these queries should work:
>
> ?- good([]).
> Yes
> ?- good([[a,b],[c,d]]).
> Yes
> ?- good([[a],[b,c]]).
> No
> ?- good([_,_,_,[a],_,_,_,[b,c],_,_,_|_]). % this one's tough.
> No
>
The last query causes problems for some of the other proposed solutions,
since they can instantiate variables to longer and longer lists. One
proposed solution used a non-logical hack (Var \= a) to avoid this. A
much nicer way is to use coroutines. Here is a solution in NU-Prolog:
?- good(LL) when LL. % not really necessary
good([]).
good(A.As) :-
all_same_length(As, A).
% all elements of first list (of lists) have same
% length as second list
% The when declaration stops the outer list being instantiated
?- all_same_length(As, _) when As.
all_same_length([], _).
all_same_length(A.As, B) :-
same_length(A, B),
all_same_length(As, B).
% two lists have the same length
% The when declaration stops the inner lists being
% instantiated unless there is another inner lists
% which is already longer.
?- same_length(L1, L2) when L1 or L2.
same_length([], []).
same_length([_|Xs], [_|Ys]) :-
same_length(Xs, Ys).
17?- good([]).
true.
18?- good([[a, b], [c, d]]).
true.
19?- good([[a], [b, c]]).
fail.
20?- good([_, _, _, [a], _, _, _, [b, c], _, _, _|_]).
fail.
21?- good([A, B, [C], D.E|F]).
Warning: Goal floundered.
F = F,
E = [],
D = D,
C = C,
B = [_WEIL],
A = [_WEIP] ;
fail.
22?-
The last goal illustrates a bit more than what was originally requested.
The system determines that A and B must be lists of length 1 and E must
be the empty list. The goal flounders because the program is smart
enough not to instantiate F (there are an infinite number of ways to do
so).
As well as the power of coroutines, the program illustrates a couple of
other points. The first is the use of an auxillary predicate
all_same_length, rather than having one predicate which looks at two
list elements at a time. There are advantages in terms of clause
indexing (no choice points are created, and no cuts are needed) and
structure creation (no extra cons cells are created, even with todays
not so clever compilers).
The second point is the representation of the length of lists. The
worst way to do it is to call length and get an integer representation
for the length of the first list, then use length again to test
subsequent lists. Using 'is' to add one to an integer is slower that
doing a simple unification in many Prolog system, and indexing is
generally more difficult with integers (typically you want to
distinguish between the zero case and non zero case). Another, more
serious problem is that you can't hold incomplete information in
integers. Consider the following query:
22?- good([[a], [a, b | T]]).
fail.
We know the second list has length greater than one, though the exact
length cannot be expressed as an integer. Using integers to represent
lists, this query will not fail unless the Prolog system has fancy
numeric constraint handling. The next most obvious alternative to
integers is to use the successor notation to represent list lengths.
In the example above the lengths are s(0) and s(s(X)), where X depends
on the length of T. Since these two terms don't unify, the goal can be
made to fail easily (and the code for calculating lenghts is simpler and
probably faster).
This representation of list lengths can still be improved on slightly.
We can use the list itself to represent the length (as in the program
above). There is no need to create any extra structures and we can use
indexing and simple unification nicely. The program does not create any
choice points, even when the inner lists are constructed (the when
declaration on same_length causes indexing on both arguments). The
program can therefore be run using stream and-parallelism, with no
changes.
lee
Technical Details
Message-ID:
<2949@munnari.oz.au>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!samsung!munnari.oz.au!leeReferences:
<11500022@hpldoda.UUCP>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!samsung!munnari.oz.au!lee From: lee@munnari.oz.au (Lee Naish) Newsgroups: comp.lang.prolog Subject: Re: A Challenge Message-ID: <2949@munnari.oz.au> Date: 21 Dec 89 06:36:45 GMT References: <11500022@hpldoda.UUCP> Sender: news@cs.mu.oz.au Reply-To: lee@munmurra.UUCP (Lee Naish) Organization: Comp Sci, University of Melbourne Lines: 115