Hi -
I guess it is incumbent on me to waste some of my own precious time
on this "easy" challenge. Did I really suggest this was easy? I
must have been out of my mind. Well, I am now. Certainly after
reading David's marvellous entry which illustrates a remarkable
approach to the problem. Wow!
My own (first) entry is included below. I decided to write a core
procedure that takes N items from the stack and deliver a
quasi-repeater. I say quasi repeater because either it returns
termin or a permutation of the original N items - as such it fails to
meet the contract of a repeater. However, this allows it to make
good use of the stack rather than the heap.
Similar to David's entry, the core procedure is fronted by a more
friendly version that handles a variety of data types and performs
the conversion to the core.
At this point I will observe that both David's solution and my own
have two defects. (1) Neither supports any store sharing between
solutions - although this can be constructed as an advantage. (2)
Neither can deal with potentially infinite dynamic lists. I wonder
if anyone can repair these problems?
---oooOOOooo---
compile_mode :pop11 +strict;
section;
;;; We want to use fast integer arithmetic, so we need to establish
;;; safe limits.
uses int_parameters;
define permutations_n( n );
lvars procedure constructor;
define exhausted_pd();
mishap( 0, 'Calling an exhausted repeater' )
enddefine;
define singleton_to_pd( x );
lvars done = false;
procedure();
if done then
exhausted_pd()
else
x;
x == termin -> done;
termin -> x;
endif
endprocedure
enddefine;
;;; Verify that it is safe to add 1 to n. We need this to
;;; support idx : integer AND idx ranges from 1 to n + 1.
fi_check( n, 0, pop_max_int fi_- 1 ) -> _;
if n == 0 then
singleton_to_pd( 0 )
else
;;; Pick up the top n-1 items. We'll extract the permutations
;;; of these items one by one. Each of these P(n-1) will
;;; be used to generate n permutations from P(n) by transposition.
lvars procedure gen = permutations_n( n fi_- 1 );
lvars item = ();
lvars vector = initv( n ); ;;; This may also
establish n < pop_max_int.
lvars idx = n fi_+ 1; ;;; 1 <= idx : integer <= n + 1
;;; Generator calls itself recursively so it needs to be named.
define generator();
if idx fi_<= n then
;;; Invariant: the elements 1 to n - 1 are a permutation of
;;; the n-1 items captured by gen. The nth element is, in
;;; principle item, BUT in practice we allow it to become
;;; junk for a small efficiency gain.
lvars ith = fast_subscrv( idx, vector );
ith -> fast_subscrv( n, vector ); ;;; Transposition.
item -> fast_subscrv( idx, vector );
destvector( vector ); ;;; PUSH THE ANSWER.
ith -> fast_subscrv( idx, vector ); ;;; restore
the invariant.
idx fi_+ 1 -> idx;
else
;;; We set up a new sub-permutation to create the
;;; next batch of permutations from.
lvars k = gen(); ;;; k is
termin OR n - 1.
if k == termin then
termin;
exhausted_pd -> gen;
;;; The vector is now garbage, too.
else
;;; Set up the invariant described about. The nth
;;; value does not matter. I use -item- here, since
;;; that is the 'natural' value. However, the nth
;;; slot gets zapped every time.
fill( item, vector ) -> _;
1 -> idx;
;;; Having set up the invariant we call the generator
;;; again to get out the values.
generator();
endif
endif
enddefine;
generator
endif;
enddefine;
define permutations( obj );
lvars ( destructor, constructor ) = (
if obj.isvectorclass then
lvars key = obj.datakey;
key.class_dest, key.class_cons
elseif obj.islist then
destlist, conslist
else
mishap( obj, 1, 'List or vectorclass object expected' )
endif
);
lvars procedure gen = permutations_n( obj.destructor );
procedure();
lvars k = gen();
if k == termin then
termin
else
constructor( k )
endif
endprocedure
enddefine;
endsection;
--
Steve
|