Re: Integration on RPL machines Message #25 Posted by James M. Prange (Michigan) on 18 Dec 2005, 6:54 a.m., in response to message #24 by Karl Schneider
Hi Karl,
Quote:
Thank you for the informative response regarding integration
tolerance, and for clearing some things up about numerical
integration on RPL-based machines.
You're welcome.
Quote:
Your response would have been helpful for my
first
RPL "challenge" in late 2003, when I didn't know how to
integrate a function defined as a program (rather than as an
expression) on an HP-48G. I got only one uninformative reply.
I guess that I thought that Veli-Pekka's response was informative
and helpful; he was referring to "user-defined functions". Or
maybe I didn't notice your question, or maybe I noticed but didn't
find the time to reply.
Quote:
You say that it is possible on the HP-28C/S, but not on the 48/49.
Unfortunately (in my opinion), that seems to be the case, at least
for numerical integration. The 49 series adds a few more commands
for integration, but I think for symbolic integration only.
Although the integrand can't be a program on the 48/49, this can
be worked around. Let me review some things about UserRPL.
Things that an RPL model can do can be called "operations".
Examples of operations include disabling last stack saves, the
ROOT command, and the SIN function. Disabling last stack saves can
be done from menu 69 (so it is an operation), but can't be done
from within a UserRPL program, so it's not a "command" (or a
"function"). ROOT can be used (with postfix notation) in a
program, so it's a "command" (as well as an "operation"), but it
can't be used in an algebraic object, so it's not a "function".
SIN can be used in an algebraic with the prefix syntax SIN(X), so
it's a "function", and it can also be used in a program using the
postfix syntax X SIN , so it's also a "command" (as well as an
"operation"). All functions can also be used as postfix commands,
although the documentation calls them functions. Some functions
require the prefix syntax, such as SIN(X), when used in an
algebraic, others require an infix notation, such as A+B, when
used in an algebraic.
But what to do when an algebraic is wanted, or even required (such
as for the integrand argument for integration), but a command that
we'd like to use in the algebraic isn't a function? We can work
around the limitation by making our own UDF (user-defined
function), that we can use with prefix syntax, such as F(x,y,z),
within an algebraic, or for that matter, with postfix syntax such
as x y z F.
When used as a postfix command, a UDF takes it's arguments from
the stack and binds the values as named local variables that can
be used in the "defining procedure", which is either an algebraic
or a program. When used within an algebraic, a UDF take it's
arguments from a comma-separated (or period-separated, if the
"fraction mark" is a comma) or semicolon-separated, parenthetical
argument list instead of from the stack.
The following assumes that the "fraction mark" is a period.
Suppose that I want the (3-dimensional) distance between points
[x1 y1 z1] and [x2 y2 z2]. I can write a UDF using an algebraic
procedure as follows:
%%HP: T(3)A(D)F(.);
\<<
\-> x1 y1 z1 x2 y2 z2
'\v/(SQ(x2-x1)+SQ(y2-y1)+SQ(z2-z1))'
\>>
'F3DISTA' STO
Now, either 'F3DISTA(0,0,0,3,4,12)' EVAL
or the sequence 0 0 0 3 4 12 F3DISTA
returns 13.
I could do much the same using a program for the defining
procedure:
%%HP: T(3)A(D)F(.);
\<<
\-> x1 y1 z1 x2 y2 z2
\<<
x2 x1 - SQ y2 y1 - SQ + z2 z1 - SQ + \v/
\>>
\>>
'F3DISTP' STO
Suppose that I want to sum the cubes of the numbers within a
range. I can write a UDF as follows:
%%HP: T(3)A(D)F(.);
\<<
\-> l h
\<<
0.
l h
FOR n
n 3. ^ +
NEXT
\>>
\>>
'FSCUBES' STO
Now 'FSCUBES(2,5)' EVAL returns 224. Of course the sequence 2 5
FSCUBES also returns 224.
Here's a UDF that takes the square root of a number less than 1,
and squares a number equal to or greater than 1, although I don't
know why anyone would want to do that.
%%HP: T(3)A(D)F(.);
\<<
\-> x
\<<
x
DUP 1.
IF
<
THEN
\v/
ELSE
SQ
END
\>>
\>>
'FSQSQR' STO
Now 'FSQSQR(.25)' EVAL returns .5, and 'FSQSQR(5)' EVAL returns 25.
ROOT seem to be a particularly difficult case. The arguments for
ROOT can be any of the following:
3: procedure (program or algebraic)
2: global name
1: guess or guesses. (1 number or a list of 1, 2, or 3 numbers)
At first glance, it looks as if we could simply define an
function, named say, 'FROOT', that we could use in an algebraic
using the syntax FROOT(procedure,global name,guess). The first
complication is that a prefix function argument list can't include
a program, so the first argument will have to be an algebraic (we
may have to make another user-defined function for the first
argument). The next is that a prefix function argument list can't
include a quoted name, so we have to be sure that the name doesn't
already exist (otherwise the contents instead of the name of the
variable would be used). Finally, the last argument can be 1, 2,
or 3 guesses, and list delimiters can't be used within a prefix
function argument list.
To work around that last argument, I'll make three separate UDFs:
%%HP: T(3)A(D)F(.);
\<<
\-> p n g
\<<
p n g
ROOT
\>>
\>>
'F1ROOT' STO
%%HP: T(3)A(D)F(.);
\<<
\-> p n g1 g2
\<<
p n
g1 g2 2. \->LIST
ROOT
\>>
\>>
'F2ROOT' STO
%%HP: T(3)A(D)F(.);
\<<
\-> p n g1 g2 g3
\<<
p n
g1 g2 g3 3. \->LIST
ROOT
\>>
\>>
'F3ROOT' STO
Now, suppose that I want to find the root near 3 of the function
SIN(X), in RAD mode. I can do any of the following:
RAD HOME 'X' PURGE 'F1ROOT(SIN(X),X,3)' EVAL
RAD HOME 'X' PURGE 'F2ROOT(SIN(X),X,3,4)' EVAL
RAD HOME 'X' PURGE 'F3ROOT(SIN(X),X,2,3,4)' EVAL
Any of the above will return 3.14159265359. Of course, if the
calculator is already in Radians mode, then RAD can be omitted,
and if I know that 'X' won't be found on the current path, I can
omit the HOME 'X' PURGE part of the sequence. Note that a new 'X'
will be stored in the home directory.
In the above, I stored the UDFs as global variables, but note that
I can also store them as local variables. For example:
%%HP: T(3)A(D)F(.);
\<<
RCLF PATH
RAD HOME
'X' PURGE
\<<
\-> p n g1 g2 g3
\<<
p n
g1 g2 g3 3. \->LIST
ROOT
\>>
\>>
\-> f3root
\<<
'f3root(SIN(X),X,2,3,4)' EVAL
\>>
EVAL
STOF
\>>
'F3LROOT' STO
Here, the UDF is f3root, and F3LROOT will return 3.14159265359,
and overwrite any variable 'X' in the home directory. It also
restores the original directory and angular unit mode.
In the 49 series, at least with recent ROMs, the PUSH and POP
commands can be used to save and restore the current flags and
directory.
Of course, I don't have to store all of the arguments to a command
in the UDF as local variables, although at least one argument has
to be a local variable for the program to be a UDF. For example:
%%HP: T(3)A(D)F(.);
\<<
\-> g
\<<
'SIN(X)' 'X' g
ROOT
\>>
\>>
'F1SINROOT' STO
Now, 'F1SINROOT(3)' EVAL returns 3.14159265359,
'F1SINROOT(6)' EVAL returns 6.28318530718,
'F1SINROOT(9)' EVAL returns 9.42477796077,
and so on. All of these overwrite any variable 'X' in the current
directory. Since I can quote X in the program, the 'X' PURGE isn't
needed.
I hope that helps.
I wrote above that disabling last stack saves isn't a command, but
on the 49 series, the KEYEVAL command offers a way to make many
operations programmable. Exceptions are operations that require
holding down the ON key while pressing another key. I've sometimes
wished for KEYEVAL on the 28 and 48 series.
Of course, SysRPL makes possible many things, and SysRPL entry
points can be called from UserRPL with the commands SYSEVAL,
LIBEVAL, and FLASHEVAL. But use these with care, as it's easy to
cause a memory clear by improper use.
Quote:
There is room on the classic 4-level RPN stack for an
input accuracy argument, and it wouldn't matter even if all four
stack levels were filled with input variables. This is because
INTEG fills the stack with the present value of the input variable
to the user-defined program, which might use all four stack
levels.
However, I believe that to include the function accuracy on the
RPN stack as an input would have been unsound, due to potential
pitfalls to the user. (Valentin disagreed with me, but I am
unconvinced. You may read the posts in this same thread for this
discussion.)
Okay; my 12C and 16C don't seem to include integration and I
really don't care to read all of the documentation for other
Classic RPN models, so I shouldn't've even commented on that
issue.
Quote:
The fundamental difference between the RPL stack and the RPN stack
(besides depth) is that the RPL stack objects have specific object
types, which the calc can check.
And in fact, every built-in UserRPL command that requires any
argument checks that the number of arguments required is
available, then checks the types of these arguments and proceeds
appropriately. If arguments that it can use aren't available, then
it will error out.
Quote:
Thus, if the user omits the function tolerance on the lowest HP-28
stack level as input to INTEG, the calc knows to return an error
(or alternatively to use a default tolerance). INTEG on the
RPN-based 34/15/41/32/33 models, however, take only floating-point
inputs from the stack. If the tolerance is not placed on the
stack, unrelated stack contents would be used as input arguments,
and possibly the stack contents would be used for the wrong input
variables.
Which brings up another difference. With Classic RPN, all of the
stack registers are always available; even after clearing, they're
zero-filled. On an RPL model, after clearing the stack, the stack
levels simply don't exist, and attempting to use a non-existent
level will cause an error.
Another difference is that typically, more stack levels are
displayed on the RPL models. As far as I know, the Classic RPN
models display at most two registers, so what's in the other
registers isn't so obvious.
Regards, James
Edited: 18 Dec 2005, 7:54 a.m.
|