Post Reply 
HP48 programming: use values from stack in expression
11-30-2018, 02:47 PM
Post: #1
HP48 programming: use values from stack in expression
Hello all

I'm trying to write a simple program for the HP48. The program should take two values from the stack and put an expression back on the stack that contains these values. For example: take 2 and 3 and return '2+3'.

With 2 and 3 as input, I tried << -> a b 'a+b' >> which returns 5 and << -> a b << 'a+b' >> >> which returns 'a+b'. And << -> a b << 'a+b' EVAL >> >> returns 5 again (of course)...

Thanks for any help!
Beat
Find all posts by this user
Quote this message in a reply
11-30-2018, 09:14 PM
Post: #2
RE: HP48 programming: use values from stack in expression
Code:
« "'" ROT + "+" + SWAP + "'" + OBJ→ »

Cheers
Thomas
Find all posts by this user
Quote this message in a reply
11-30-2018, 10:21 PM
Post: #3
RE: HP48 programming: use values from stack in expression
Code:
<< -> A B << 'A+B' 'A' A = SUBST 'B' B = SUBST >> >>
Visit this user's website Find all posts by this user
Quote this message in a reply
12-01-2018, 05:05 AM (This post was last modified: 12-06-2018 02:10 AM by Giuseppe Donnini.)
Post: #4
RE: HP48 programming: use values from stack in expression
SUBST is not an HP-48 command.

Thomas Klemm's solution works, but you would have to rewrite the program for every single function you want to apply.

Here's a more general solution:

1. For single-argument functions (like SIN, square root, etc.):
Code:
@ F1 : ( x {f} --> f(x) )
@ Given x and f on the stack, where x is any argument, and f is a function of
@ one argument, F1 returns 'f(x)'.  Note: f has to be embedded in a list to
@ prevent its immediate execution.
@ Example: ( 12 {SIN} --> 'SIN(12)' )

\<< SWAP \-> a                        @ Save argument into local variable a
    \<< 'a' SWAP EVAL                 @ Calculate f(a) symbolically
        'a' a 2 \->LIST \|^MATCH DROP @ Substitute x for a
    \>>
\>>

2. For two-argument functions (like +, -, etc.):
Code:
@ F2 : ( x y {f} --> f(x,y) )
@ Given x, y, and f on the stack, where x and y are any arguments, and f is a
@ function of two arguments, F2 returns 'f(x,y)'.  Note: f has to be embedded in
@ a list to prevent its immediate execution.
@ Example: ( 1 6 {+} --> '1+6' )

\<< 3 ROLLD \-> a b                   @ Save arguments into local vars a and b
    \<< 'a' 'b' ROT EVAL              @ Calculate f(a,b) symbolically
        'a' a 2 \->LIST \|^MATCH DROP @ Substitute x for a
        'b' b 2 \->LIST \|^MATCH DROP @ Substitute y for b
    \>>
\>>

Following the same pattern, programs for three or more argument functions can easily be added.

Note that these programs respect the symbolic result flag -3: if clear (default), they return symbolic results; if set, they return numeric results.
Find all posts by this user
Quote this message in a reply
12-01-2018, 06:12 AM
Post: #5
RE: HP48 programming: use values from stack in expression
(12-01-2018 05:05 AM)Giuseppe Donnini Wrote:  SUBST is not an HP-48 command.
It is not? Used the 50G too much recently, sorry. Big Grin
Visit this user's website Find all posts by this user
Quote this message in a reply
12-01-2018, 02:06 PM (This post was last modified: 12-01-2018 09:51 PM by Thomas Klemm.)
Post: #6
RE: HP48 programming: use values from stack in expression
(12-01-2018 05:05 AM)Giuseppe Donnini Wrote:  
Code:
\<< 1 GET                             @ Get unevaluated function from list

We don't need that step as EVAL takes care of the list:

'a'
'b'
{ + }
EVAL
'a+b'

Nice solution, by the way. I must admit I wasn't aware of neither ↑MATCH nor ↓MATCH.

I couldn't come up with a simple way to create something like this map based on 2 and 3:
Code:
{ { a 2 } { b 3 } }

But that allowed us to extract the repeated code and use DOLIST:

'a+b'
{ { a 2 } { b 3 } }
1
« ↑MATCH DROP »
DOLIST
'2+3'

Any suggestions on how to do that?

Or then rather use STREAM:

{ 'a+b' { a 2 } { b 3 } }
« ↑MATCH DROP »
STREAM
'2+3'

Cheers
Thomas
Find all posts by this user
Quote this message in a reply
12-02-2018, 11:03 AM
Post: #7
RE: HP48 programming: use values from stack in expression
Thank you for all the hints. At the moment the string solution with OBJ→ is sufficient for me, but I like the additional possibilities with lists and ↑MATCH... Thanks again!
Find all posts by this user
Quote this message in a reply
12-04-2018, 02:44 AM (This post was last modified: 12-05-2018 01:15 PM by Giuseppe Donnini.)
Post: #8
RE: HP48 programming: use values from stack in expression
But of course! Lists become procedure class objects when "evaluated"--as opposed to being merely "executed". Excellent suggestion, Thomas, thanks! I already changed my post accordingly.

For multiple variable substitutions, I use the following program which indeed relies on MATCH and works almost the same as your DOLIST example, except that it uses an explicit loop.

Code:
@ SUBST (version A)
@ ( 'old' { { name1 val1 } { name2 val2 } ... { namen valn } } --> 'new' )
@ Example: ( 'X+Y' { { X 1 } { Y 2 } } --> '1+2' )
\<<
  REVLIST         @ Needed since sublists will be handled in reverse order (*).
  OBJ\->          @ Explode list.
  DUP 2 + ROLL    @ Roll original expression to level 1.
  SWAP 1 SWAP     @ Set up loop counter.
  START           @ Repeat n times:
    SWAP          @ - Get next sublist.
    \|^MATCH DROP @ - Do substitution.
  NEXT
\>>

@ (*) Without REVLIST, one would get
@       ( 'X+Y' { { X Y+1 } { Y 2 } } --> 'Y+1+2' )
@     instead of
@       ( 'X+Y' { { X Y+1 } { Y 2 } } --> '2+1+2' )
@     because the Y=2 substitution would be done first.

MATCH is a very powerful command, whose full potential I did not realize until studying Bill Wickes' "Insights" books. Because it uses MATCH, SUBST is in fact much more flexible than it at first appears. Actually, its level 1 argument allows the more general form:

{ { 'pattern1' 'replacement1' } ... { 'patternn' 'replacementn' } }

of which

{ { name1 value1 } ... { namen valuen } }

is only a small subset. You can therefore easily replace entire sub-expressions, as in the following example:

( 'COS(X)/SIN(X)+3*Y' { { 'COS(X)/SIN(X)' 'COT(X)' } { Y 5 } } --> 'COT(X)+3*5' )

The most powerful feature of MATCH, however, is that it allows you to use wild cards to target specific sub-expressions inside the main algebraic expression. Any name that begins with the ampersand character "&" is interpreted as a wild card. Suppose you have the following expression:

'(SIN(X^2+1/Y))'


and you want to replace X with 9, Y with 2, and then expand the sum according to the addition law for sines. This is easily done with the following level 1 argument:

{ { X 9 } { Y 2 } { 'SIN(&1+&2)' 'SIN(&1)*COS(&2)+COS(&1)*SIN(&2)' } }


The result will be:

'SIN(9^2)*COS(1/2)+COS(9^2)*SIN(1/2)'


Or, suppose you want to replace every square root in an expression with a cubic root. This simple level 1 argument: { { '\v/&1' 'XROOT(3,&1)' } } will carry out all the replacements.

As a further refinement, you may specify an optional third algebraic in each list which, when evaluated, must return a user flag determining whether the replacement should take place or not. For example, while simplifying square roots of squares, you may want to make sure that the arguments of the square function are actually positive:

{ { '\v/(&1^2)' &1 '&1\>=0' } }

Since the optional test argument is less often needed, and since one might prefer a more mathematical look and feel, here's an alternative version of SUBST, which uses a list of equations instead of a list of lists as its level 1 argument. For convenience, a single equation may also be entered without list delimiters.

Code:
@ SUBST (version B)
@ [A] ( 'old' 'name=value' --> 'new' )
@ [B] ( 'old' { 'name1=val1' 'name2=val2' ... 'namen=valn' } --> 'new' )
@ Examples: ( 'SIN(X)' 'X=30' --> 'SIN(30)' )
@           ( 'X+Y' { 'X=1' 'Y=COS(60)' } --> '1+COS(60)' )
\<<
  IF DUP TYPE 9 SAME @ If single equation (case A),
  THEN { } + END     @   embed it in a list (reduction to case B).
  REVLIST            @ Needed since equations will be handled in reverse order.
  OBJ\->             @ Explode list.
  DUP 2 + ROLL       @ Roll original expression to level 1.
  SWAP 1 SWAP        @ Set up loop counter.
  START              @ Repeat n times:
    SWAP             @ - Get next equation.
    EQ\-> 2 \->LIST  @ - Take it apart and build list for MATCH.
    \|^MATCH DROP    @ - Do substitution.
  NEXT
\>>

The more elaborate constructs described above, involving entire expressions and wild cards, are also available in this version of SUBST, albeit with the following restrictions:
1. Since the list elements have the general form 'pattern=replacement', pattern and replacement may not be equations themselves.
2. The optional test argument is not available.
Find all posts by this user
Quote this message in a reply
12-05-2018, 04:11 AM
Post: #9
RE: HP48 programming: use values from stack in expression
That was an interesting read. I wasn't aware of these pattern matching capabilities of the HP-48.

Thanks a lot!
Thomas
Find all posts by this user
Quote this message in a reply
12-05-2018, 03:49 PM
Post: #10
RE: HP48 programming: use values from stack in expression
Another interesting use of MATCH is its combination with UDF (User Defined Function) versions of RPN-only commands, allowing you to select sub-expressions within larger expressions as arguments for those rewritten commands. In other words, it allows you to perform RULES-type operations right on the stack or inside programs. Here's an example with COLCT.

Code:
@ NAME     : SCOLCT
@ ABSTRACT : Performs selective COLCT on a specified sub-expression.
@            SCOLCT takes a symbolic expression 'symb1' from stack level 2 and
@            a symbolic pattern 'symb.pat' from stack level 1 and performs a
@            selective COLCT within 'symb1' on the sub-expression specified by
@            the wildcard name '&.' in 'symb.pat' ("." was chosen because it
@            lies on a primary key and is hardly ever used as a variable name).
@            Names are left unevaluated, whether a corresponding variable
@            exists in the current path or not; in other words, they are
@            treated as formal variables.
@ STACK    : ( 'symb1' 'symb.pat' --> 'symb2' )
@ EXAMPLES : ( '2*X+3*X-COS(4*X+5*X)+SIN(6*X+7*X)' 'COS(&.)' -->
@                                             '2*X+3*X-COS(9*X)+SIN(6*X+7*X)' )
@            ( 'SIN(2*X+3*X)/(4*X+5*X)' '&1/&.' --> 'SIN(2*X+3*X)/(9*X)' )
\<<
  \<< \-> x \<< x COLCT \>> \>>   @ Build embedded UDF (User Defined Function).
  \-> f                           @ Save UDF in local variable.
  \<< DUP \->STR                  @ Decompile copy of 'symb.pat'.
      DUP "&." POS                @ Find "&." token.
      OVER OVER 1 - 1 SWAP SUB    @ Get head part of string without "&." token.
      "f(&.)" +                   @ Replace "&." with "f(&.)".
      ROT ROT 2 + MAXR \->NUM SUB @ Get tail part of string.
      +                           @ Append tail part.
      OBJ\->                      @ Recompile string; use it as 'symb.repl'.
      2 \->LIST                   @ Build list argument for MATCH.
      \|^MATCH DROP               @ Replace. (Use \|vMATCH for top-down repl.)
      #64037h SYSEVAL             @ Prevent global name resolution by executing
                                  @   SysRPL word WithHidden, which temporarily
                                  @   sets both context and stopsign to the
                                  @   Hidden Directory for the duration of the
                                  @   next object's (EVAL) execution.  N.B.
                                  @   WithHidden is a supported entry point.
      EVAL                        @ Perform selective COLCT by applying UDF;
                                  @   reset context & stopsign to current dir.
  \>>                             @ Discard UDF.
\>>
Find all posts by this user
Quote this message in a reply
Post Reply 




User(s) browsing this thread: 3 Guest(s)