[Date Prev] [Date Next] [Thread Prev] [Thread Next] Date Index Thread Index Search archive:
Date:Mon Jan 9 12:40:16 2003 
Subject:Re: Silly Pop-11 Programming Challenge 2003 
From:steve 
Volume-ID:1030109.01 

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