Path: utzoo!attcan!uunet!mcvax!tuvie!tuhold!thom
From: thom@tuhold (Thom Fruehwirth)
Newsgroups: comp.lang.prolog
Subject: more on permutations
Message-ID: <1161@tuhold>
Date: 17 Aug 88 11:18:25 GMT
Organization: Institut f. Angewandte Informatik, TU Vienna
Lines: 78


When commenting on permutations the last time I asked if it is 
possible to write a permutation predicate using only a single 
predicate permute/n for its definition. Richard O'Keefe in article
<209@quintus.UUCP> did it by a welldone transformation from the 
initial permute/2 predicate ending up in permute/6.

I agree with him that:
>I don't think it's particularly useful to have permutation done 
>with only one recursive predicate, but I think the derivation of 
>the result may be of interest.

Anyway, here is my version:

permute(A,B) :- permut
permute([],[],[]).
permute([X|A],[X|B],C) :- B=[_|_],permute(A,B,C);permute(C,B,A).
permute([X|A],B,C) :- permute(A,B,[X|C]).

The explicit unification in the second clause of permute/3 avoids 
duplication of every solution (this happens as both subgoals of 
the disjunction can match the first, base clause). This can also
be avoided by partial evaluation resulting in:

perm11([],[]).
perm11(A,B) :- perm11(A,B,[]).

perm11([X],[X],[]).
perm11([X|A],[X|B],C) :- perm11(A,B,C);perm11(C,B,A).
perm11([X|A],B,C) :- perm11(A,B,[X|C]).

The idea of permute/3 is that the third argument is an intermediate 
storage for elements of the first argument. So the head X of the 
first argument is either passed to the resulting second argument 
directly or to the third argument for later use. The third argument 
is utilized by simply exchanging it with the first argument 
(expressed by the disjunction in the second clause).

In article <1394@kulcs.kulcs.uucp> bimbart@kulcs.uucp (Bart Demoen) 
also uses same_length/2 to ensure commutative behaviour of perm/2:

>    permute_6(X, Y) :-
>         same_length(X,Y),
>         permute_2(X,Y) .
>
>   same_length([], []) .
>    same_length([X|L1],[Y|L2]) :-
>              same_length(L1,L2) .
>
>    permute_2([], []).
>    permute_2([X|Xs], Ys1) :-
>         permu>         select(X, Ys1, Ys).

and adds:
> permute_6 is symmetric, so it is easier to understand that it 
> can be used in a symmetric way

But permute_2 itself isn't symmetric. Here is a full symmetric 
version of permute_2/2 (unfortunately non-commutative and 
tail-recursive thus inefficient):
   
permute_2([],[]).
permute_2([X|Xs],[X|Ys]):-
	permute_2(Xs,Ys).
permute_2([X|Xs],[Y|Ys	select(X,Ys,Ys1),
	select(Y,Xs,Xs1),
	permute_2(Xs1,Ys1).

Note that the first two clauses are equivalent to same_length/2.

I conclude the discussion on permutations has shown two things:
-for determinstic predicates use tail recursion, 
  for indeterministic predicates left recursion is more efficient
  (this was pointed out by Bart Demoen)
- it is fun to play around with predic  Sometimes its even useful.

thom fruehwirth
technical university of vienna