[comp.lang.prolog] Swapping list elements

ken@aiva.ed.ac.uk (Ken Johnson) (08/31/88)

<<<  CUT HERE: Below this line,this entire file is Prolog executable.
%
% Having once joined in a correspondence about arbitrarily re-arranging
% the elements of lists in linear time, it occurs to me that one might
% flatten the response time by converting the list to a term, exchanging
% elements of the term and building up a new one then converting the term
% back to a list.  Converting term to list and back should take order N;
% accessing an element in a term with `arg' is done in constant time (on
% our system, anyway). 
% 
% It's also an interesting programming puzzle, so stop reading where I've
% put ^Ls if you want to try it yourself. (For people new to the Net:
% when ^L appears at the bottom left of your screen, you have to hit the
% top of the terminal really hard with your fist before it will show you
% the next page.)
% 
% Digression: Of course, the question that goes before this is: Do you really need
% to use lists? The Pascal- or Basic-like algorithm assumes you are using
% arrays, that is, at the time you want  to do the permutation you want the
% sets of objects to have array-like properties not list-like ones.
% So maybe you could speed up your program by using terms throughout instead
% of lists.
%
% Let us return to our muttons. The programming puzzles that arise are...
%
% Question 1: Given a list of ground* terms, convert them into a term (e.g. 
% list_to_term([p,q,r,s],Term) should instantiate Term to
% '$list'(p,q,r,s).  I don't care what the functor is. (I am sure it is also
% possible to have uninstantiated variables too but it's too hard for me,
% especially with lists like [A,B,A,C,C] with repeated uninstantiated
% variables! So this isn't guaranteed in such cases.)
% 
% Hard way:

list_to_term(List,Term) :-
	list_to_term(0,List,Term).

list_to_term(M,[H|T],Term) :-
	N is M + 1,
	list_to_term(N,T,Term),
	arg(N,Term,H).

list_to_term(M,[],Term) :-
	functor(Term,'$list',M).

% Easy way:

list_to_term_the_easy_way(List,Term) :-
	Term =.. ['$list'|List].

% Question 2: Given a term of the form generated by list_to_term,
% recover the list that generated it. E.G. `term_to_list('$list'(p,q,r),L)
% should instantiate L to [p,q,r].
% 
% Hard way:

term_to_list(Term,List) :-
	functor(Term,_,N),
	term_to_list(1,N,Term,A-A,List-[]).

term_to_list(M,N,_,A-B,A-B) :-
	M > N.

term_to_list(M,N,Term,A-[Arg|B],C-D) :-
	M =< N,
	arg(M,Term,Arg),
	M1 is M + 1,
	term_to_list(M1,N,Term,A-B,C-D).

% Easy way:
% list_to_term_the_easy_way is bidirectional (above) !.
%
% Question 3:
% In the permutation problem you are going to need both a term whose
% args are the same as the input list, and a term whose args are the
% same as the output list. You can generate them both at the same time
% like this.
% Usage: list_to_term_with_copy([p,q,r],To,Ct,Cl) will instantiate
% To = '$list'(p,q,r), Ct = '$list'(_1,_2,_3), Cl = [_1,_2,_3].
% 
% I can't see a fast easy way to do it with =.., though doubtless someone
% else will!

list_to_term_with_copy(List_in,Term_out,Copy_term,Copy_list) :-
	list_to_term_with_copy(0,List_in,Term_out,Copy_term,Copy_list).

list_to_term_with_copy(M,[],Term_out,Copy_term,[]) :-
	functor(Term_out,'$list',M),
	functor(Copy_term,'$list',M).

list_to_term_with_copy(M,[H|T],Tm_out,Cp_term,[New|Cp_list]) :-
	N is M + 1,
	list_to_term_with_copy(N,T,Tm_out,Cp_term,Cp_list),
	arg(N,Tm_out,H),
	arg(N,Cp_term,New).

% Now, once you know what you want to exchange, you need only do

exchange_elements(I,J,Term1,Term2) :-
	arg(I,Term1,Arg),
	arg(J,Term2,Arg).

% and the copied list will come right of its own accord.
-- 
==============================================================================
From:       Ken Johnson (Added ingredient: 50% Bicycle)
Address:    AI Applications Institute, The University, EDINBURGH
Phone:      031-225 4464 ext 212
Email:	    k.johnson@ed.ac.uk
Disclaimer: These are my opinions. If you don't like them, I have others.