[comp.sys.handhelds] Breaking out of loops on the HP-48SX

madler@piglet.caltech.edu (Mark Adler) (11/11/90)

I just discovered a technique that I thought I'd share.  The HP-48SX
appears to lack a way to break out of loops.  But appearances can be
deceiving.  There is a kludgey way to do it.  You can put the loop
in an IFERR statement, and then divide by zero or some such thing to
cause an error.  This appears to cleanly exit the loop (or loops).
As an example of this tehcnique, here is a program (FACNUM) which
will take a whole number and return its prime factorization as a
list of primes, repeated if necessary.  I wanted to avoid having the
inner loop do two tests, so it could be a simple FOR loop, and so
the loop exit is done inside IF statements with a divide by zero.

A note to HP: please include a throw/catch operation for exiting
loops on the next calculator (48SY?  49?).  The code to handle
cleaning up leftover constructs appears to already be there for
IFERR.  Thanks.

Mark Adler
madler@piglet.caltech.edu

program follows ...

%%HP: T(3)A(R)F(.);
@ FACNUM (BYTES = 277.5, #74FFh)
@ Given an integer, return its prime factorization as a list.
@ For example, 16353 FACNUM returns { 3 3 23 79 }.
\<<
  @ initialize factor list
  { } SWAP

  @ For this part of the program the stack is: n factorlist, where the two are
  @ kept so that the product of the list times n is the original integer.

  @ factor out 2's
  WHILE DUP 2 MOD NOT REPEAT
    2 / SWAP 2 + SWAP END

  @ factor out 3's
  WHILE DUP 3 MOD NOT REPEAT
    3 / SWAP 3 + SWAP END

  @ start factor search at 5
  5

  @ The stack is now: k n factorlist, where the second two are maintained as
  @ before, and k is the largest 5 mod 6 integer less than or equal to the
  @ last factor found (execpt initially when it is set to 5).

  @ search from k to sqrt(n) for factors---k must be 5 mod 6
  WHILE OVER 1 \=/ REPEAT	@ do while n is not one
    OVER \v/ FLOOR		@ go up to the floor of the square root
    IFERR			@ (divide by zero used as a loop breaker)
      FOR i			@ look at factors that are 1 and 5 mod 6
	IF DUP i MOD NOT THEN
	  i 0 / END		@ if 5 mod 6 divides n, then cause error
	IF DUP i 2 + MOD NOT THEN
	  i 2 + 0 / END		@ if 1 mod 6 divides n, then cause error
      6 STEP
    THEN DROP			@ got an error---trash the zero
    ELSE DUP			@ end of loop---n divides n
    END				@ after this, stack is: factor n factorlist
    ROT OVER + ROT ROT		@ add factor to list (factor n factorlist')
    SWAP OVER / SWAP		@ divide out divisor (factor n' factorlist')
    DUP 6 MOD 5 - 2 / +		@ set k' to largest 5 mod 6 <= divisor
  END				@ find next factor (k' n' factorlist')

  @ return list, dropping n and k
  DROP2
\>>

edp@ipl31.enet.dec.com (Eric Postpischil (Always mount a scratch monkey.)) (11/12/90)

In article <1990Nov11.002758.4933@nntp-server.caltech.edu>,
madler@piglet.caltech.edu (Mark Adler) writes:

> You can put the loop in an IFERR statement, and then divide by
> zero or some such thing to cause an error. 

To generate an error, you can use the DOERR command.


				-- edp

madler@piglet.caltech.edu (Mark Adler) (11/12/90)

Eric Postpischil points out:
>madler@piglet.caltech.edu (Mark Adler) writes:
>
>> You can put the loop in an IFERR statement, and then divide by
>> zero or some such thing to cause an error. 
>
>To generate an error, you can use the DOERR command.
>
>
>				-- edp


Thanks.  A "0 DOERR" is much cleaner, in that it does not affect ERRN or
ERRM (which records the last error).

Mark Adler
madler@piglet.caltech.edu