There are several methods to make use of CAS commands in a program on the HP Prime.
1. CAS("command(arg1,arg2,...,argn)")
2. CAS.command("arg1","arg2",...,"argn") or CAS.command("arg1,arg2,...,argn")
3. Create an actual CAS function via #cas and #end
Recall that an HP Prime Programming Language (HPPPL) program is essentially a collection of commands that are executed in the Home view. And in the Home view, most CAS commands must be treated with care. Techniques 1 and 2 behave mostly the same, and are generally used within HPPPL programs to gain access to CAS commands. Technique 3 is equivalent to creating a function in the CAS view, but the function remains resident in storage even after the use of the 'restart' command. This needs further clarification: the program's source remains in storage, however the command must be "re-compiled" in order to reuse it. Thus, technique 3 is more akin to creating a macro file that needs to be run (i.e. "compiled") after each instance of the 'restart' command. This is the advantage of creating an HPPPL program that uses techniques 1 and 2 since HPPPL programs are always resident in memory after compilation (there is no 'restart' equivalent for Home view).
Example of Technique 1
Suppose we wish to create a program that takes a formula for the function \( f(x) \) and determine the values of \( x \) such that \( f'(x) = 0 \). For our example, let \( f(x) = x^3-3x \). If we do this by hand in the CAS view, we would likely type in:
Code:
f:=x^3-3*x;
derf:=diff(f,x);
solve(derf,x);
and obtain the list: { -1, 1 }. We could simplify these steps into:
solve(diff(f,x),x);
This simplification allows us to create the following program:
Code:
EXPORT CASCMD1(f)
BEGIN
local cmd:="solve(diff(" + f + ",x),x)";
CAS(cmd);
END;
To actually use the program, we must type: CASCMD1("x^3-3*x"). Notice that the argument f is actually a string representing the expression \( x^3-3x \). Some important points:
- We CANNOT use CAS("solve(diff" + f + ",x),x)") in place of CAS(cmd). That is, the argument of CAS() must either be a string or a variable whose content is a string, and the string must be a valid CAS input. The argument of the CAS() command must not be an expression, since the CAS() command will then simply evaluate the expression in the CAS view -- i.e. it will simply add the three strings using the CAS view rules.
- It is possible to save the results using, say, L1:=CAS(cmd); to save the results into the built-in list L1. More generally, one may store the result into any variable whether local or global or built-in.
- Sometimes the "type" of the result may be altered due to differences in how the CAS and Home views represent that type. For example, one may create a symbolic matrix for the input variable f (in the form of a string) and the result is converted into a list of lists. Accessing the entries is still the same, but any type checks will require extra care.
- If we have result:=CAS(cmd); then we can (generally) use 'result' without having to convert it into a string.
- We can create a CAS variable via: CAS("casvar:=0"); -- of course, change 'casvar' accordingly.
Example of Technique 2
Again, under the same premise as in the previous example, we can create the following code:
Code:
EXPORT CASCMD2(f)
local result:=CAS.diff(f,"x");
result:=CAS.solve(result,"x");
END;
We can use 'result' directly with the solve() command. If, however, we had wanted to solve the equation \( f'(x) = -3 \), or equivalently \( f'(x) + 3 = 0 \) then it is NOT simply a matter of changing the last line of code to: result:=CAS.solve(result+3,"x"); because the expression result+3 is computed as if we were in the Home view. And in the Home view, the content of 'result' -- which is the expression '3*x^2-3' -- must be resolvable. If the variable 'x' does not exist, then an error occurs. So the following modification is required:
Code:
// modification so that we solve f'(x)=-3
EXPORT CASCMD2(f)
local result:=CAS.diff(f,"x");
result:=STRING(result) + "+3"; // or simply result:=STRING(result) + 3; since HPPPL can auto-convert where there is no ambiguity
result:=CAS.solve(result,"x");
END;
Example of Technique 3
The simplest (in terms of coding and legibility of code) is by creating a CAS function. The easiest way to create a CAS program is to simply "solve" our problem in the CAS view. Then, open the program editor and create a new CAS program. Finally, simply copy and paste (from the CAS view) the relevant commands used in the previous session.
Code:
#cas
CASCMD3(f):=
BEGIN
derf:=diff(f,x);
solve(derf,x);
END;
#end
Some important notes:
- This program will create a CAS variable named derf. If we want derf to be only temporary, then we can create a local variable named derf.
- Local variables must necessarily be declared first (it seems). Otherwise a syntax error occurs. Moreover, we cannot combine a declaration with an assignment as in non-CAS programs. So local derf:=<blah> would not be allowed. Below is an example of the use of a local CAS variable.
Code:
#cas
CASCMD3(f):=
BEGIN
local derf;
derf:=diff(f,x);
solve(derf,x);
END;
#end
- In the CAS view, the input to our program can be typed without the " symbol. In the Home view, our program (being a CAS command) requires that the input be quoted with the " symbol.
The Hybrid Approach
It is also possible to create a hybrid project. That is, a single source file could potentially have both non-CAS programs _and_ CAS programs. For example:
Code:
EXPORT NONCAS(f)
BEGIN
CAS.diff(f,"x");
END;
#cas
CASPROG(f):=
BEGIN
local result;
result:=NONCAS(f);
solve(result,x);
END;
#end
CASPROG("x^3-3*x") from the Home view will return { -1, 1 }.
Pros and Cons
There are benefits as well as drawbacks to using each technique. As of firmware 6975:
- Local variables in CAS programs MUST be declared at the beginning of each procedure. Moreover, the declarations must be separate from initialization. So whereas local foo:=bar; is completely fine for non-CAS programs, this single line must be split into local foo; foo:=bar; for CAS programs.
- CAS programs may be programmed to have dynamic input. For example,
Code:
#cas
myprog(args):=
BEGIN
local s:=SIZE(args);
CASE
IF s==0 THEN
// code for when no arguments supplied
END;
IF s==1 THEN
// code for when a single argument is supplied
END;
END;
END;
#end
may be called with myprog(), myprog(arg1), myprog(arg1,arg2), ... etc. Note that a more complete check would also include the TYPE() of the variable s (since using SIZE() on matrices will return the result: [ row column ]). The program must, of course, properly handle the various combinations of inputs. In order to achieve an equivalent program in HPPPL, we could implement something like:
Code:
EXPORT myprogram(arg)
BEGIN
local s:=SIZE(arg);
CASE
IF s==1 THEN
// we have a non-list argument; perhaps dispatch based on type of argument
END;
IF s>1 THEN
// we have a list; dispatch based on size of list
CASE
IF s==2 THEN
// blah
END;
IF s==3 THEN
// blah
END;
END; // inner case
END; // if s>1
END; // outer case
END;
This program would be called with myprog(arg1) OR myprog({arg1}), myprog({arg1,arg2}), myprog({arg1,arg2,arg3}), etc. Unfortunately there does not seem to be a way to accept the hybrid case: both no argument or variable number of arguments.
- Non-CAS programs currently have a more usable (though still broken) debugger.
- Non-CAS programs do not have to be "recompiled"; CAS programs must be "recompiled" should one use the restart command in CAS view.
- Pure CAS programs do not have to deal with type conversion between CAS and non-CAS programs.
- Non-CAS programs that use CAS commands will generally have to involve string manipulation.
- Non-CAS programs can make use of "interface" commands such as CHOOSE() and INPUT() whereas CAS programs are generally not interactive. A workaround is to use the EXPR() command to call these commands indirectly. EXPR() works like CAS() in that both take input strings.
- Both non-CAS and CAS programs must carefully account for the fact that some commands are case sensitive. Generally speaking, upper-case commands are Home-view commands whereas lower-case commands are CAS-view commands. In HPPPL, most of the time, there is no distinction between cases. That is, the command TYPE() and type() are no different. However, CAS-only commands must be typed in lower-case (e.g. mat2list() is a CAS command that works in Home view, but only if typed as mat2list() and not MAT2LIST() -- the latter does not exist). CAS programs, however, DO distinguish between TYPE() and type().
- All variables not declared as local are considered global CAS variables and persist even after the program finishes. Thus, programmers may possibly have to use purge() to clean up any mess their programs leave.
- The equality == behaves differently in CAS vs non-CAS. For example: in the CAS view, testing equality between a list and a real value returns a 0 (due to type checking, perhaps?) whereas in the Home view testing equality between a list and a real value applies the test to the entire list, resulting in a list of 0's and 1's.
- I'm sure there are more....