mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
177 lines
7.0 KiB
Plaintext
177 lines
7.0 KiB
Plaintext
Using objects
|
|
|
|
Objects are user-defined types which are associated with user-
|
|
defined functions to manipulate them. Object types are defined
|
|
similarly to structures in C, and consist of one or more elements.
|
|
The advantage of an object is that the user-defined routines are
|
|
automatically called by the calculator for various operations,
|
|
such as addition, multiplication, and printing. Thus they can be
|
|
manipulated by the user as if they were just another kind of number.
|
|
|
|
An example object type is "surd", which represents numbers of the form
|
|
|
|
a + b*sqrt(D),
|
|
|
|
where D is a fixed integer, and 'a' and 'b' are arbitrary rational
|
|
numbers. Addition, subtraction, multiplication, and division can be
|
|
performed on such numbers, and the result can be put unambiguously
|
|
into the same form. (Complex numbers are an example of surds, where
|
|
D is -1.)
|
|
|
|
The "obj" statement defines either an object type or an actual
|
|
variable of that type. When defining the object type, the names of
|
|
its elements are specified inside of a pair of braces. To define
|
|
the surd object type, the following could be used:
|
|
|
|
obj surd {a, b};
|
|
|
|
Here a and b are the element names for the two components of the
|
|
surd object. An object type can be defined more than once as long
|
|
as the number of elements and their names are the same.
|
|
|
|
When an object is created, the elements are all defined with zero
|
|
values. A user-defined routine should be provided which will place
|
|
useful values in the elements. For example, for an object of type
|
|
'surd', a function called 'surd' can be defined to set the two
|
|
components as follows:
|
|
|
|
define surd(a, b)
|
|
{
|
|
local x;
|
|
|
|
obj surd x;
|
|
x.a = a;
|
|
x.b = b;
|
|
return x;
|
|
}
|
|
|
|
When an operation is attempted for an object, user functions with
|
|
particular names are automatically called to perform the operation.
|
|
These names are created by concatenating the object type name and
|
|
the operation name together with an underscore. For example, when
|
|
multiplying two objects of type surd, the function "surd_mul" is
|
|
called.
|
|
|
|
The user function is called with the necessary arguments for that
|
|
operation. For example, for "surd_mul", there are two arguments,
|
|
which are the two numbers. The order of the arguments is always
|
|
the order of the binary operands. If only one of the operands to
|
|
a binary operator is an object, then the user function for that
|
|
object type is still called. If the two operands are of different
|
|
object types, then the user function that is called is the one for
|
|
the first operand.
|
|
|
|
The above rules mean that for full generality, user functions
|
|
should detect that one of their arguments is not of its own object
|
|
type by using the 'istype' function, and then handle these cases
|
|
specially. In this way, users can mix normal numbers with object
|
|
types. (Functions which only have one operand don't have to worry
|
|
about this.) The following example of "surd_mul" demonstrates how
|
|
to handle regular numbers when used together with surds:
|
|
|
|
define surd_mul(a, b)
|
|
{
|
|
local x;
|
|
|
|
obj surd x;
|
|
if (!istype(a, x)) {
|
|
/* a not of type surd */
|
|
x.a = b.a * a;
|
|
x.b = b.b * a;
|
|
} else if (!istype(b, x)) {
|
|
/* b not of type surd */
|
|
x.a = a.a * b;
|
|
x.b = a.b * b;
|
|
} else {
|
|
/* both are surds */
|
|
x.a = a.a * b.a + D * a.b * b.b;
|
|
x.b = a.a * b.b + a.b * b.a;
|
|
}
|
|
if (x.b == 0)
|
|
return x.a; /* normal number */
|
|
return x; /* return surd */
|
|
}
|
|
|
|
In order to print the value of an object nicely, a user defined
|
|
routine can be provided. For small amounts of output, the print
|
|
routine should not print a newline. Also, it is most convenient
|
|
if the printed object looks like the call to the creation routine.
|
|
For output to be correctly collected within nested output calls,
|
|
output should only go to stdout. This means use the 'print'
|
|
statement, the 'printf' function, or the 'fprintf' function with
|
|
'files(1)' as the output file. For example, for the "surd" object:
|
|
|
|
define surd_print(a)
|
|
{
|
|
print "surd(" : a.a : "," : a.b : ")" : ;
|
|
}
|
|
|
|
It is not necessary to provide routines for all possible operations
|
|
for an object, if those operations can be defaulted or do not make
|
|
sense for the object. The calculator will attempt meaningful
|
|
defaults for many operations if they are not defined. For example,
|
|
if 'surd_square' is not defined to square a number, then 'surd_mul'
|
|
will be called to perform the squaring. When a default is not
|
|
possible, then an error will be generated.
|
|
|
|
Please note: Arguments to object functions are always passed by
|
|
reference (as if an '&' was specified for each variable in the call).
|
|
Therefore, the function should not modify the parameters, but should
|
|
copy them into local variables before modifying them. This is done
|
|
in order to make object calls quicker in general.
|
|
|
|
The double-bracket operator can be used to reference the elements
|
|
of any object in a generic manner. When this is done, index 0
|
|
corresponds to the first element name, index 1 to the second name,
|
|
and so on. The 'size' function will return the number of elements
|
|
in an object.
|
|
|
|
The following is a list of the operations possible for objects.
|
|
The 'xx' in each function name is replaced with the actual object
|
|
type name. This table is displayed by the 'show objfuncs' command.
|
|
|
|
Name Args Comments
|
|
|
|
xx_print 1 print value, default prints elements
|
|
xx_one 1 multiplicative identity, default is 1
|
|
xx_test 1 logical test (false,true => 0,1),
|
|
default tests elements
|
|
xx_add 2
|
|
xx_sub 2 subtraction, default adds negative
|
|
xx_neg 1 negative
|
|
xx_mul 2
|
|
xx_div 2 non-integral division, default multiplies
|
|
by inverse
|
|
xx_inv 1 multiplicative inverse
|
|
xx_abs 2 absolute value within given error
|
|
xx_norm 1 square of absolute value
|
|
xx_conj 1 conjugate
|
|
xx_pow 2 integer power, default does multiply,
|
|
square, inverse
|
|
xx_sgn 1 sign of value (-1, 0, 1)
|
|
xx_cmp 2 equality (equal,non-equal => 0,1),
|
|
default tests elements
|
|
xx_rel 2 inequality (less,equal,greater => -1,0,1)
|
|
xx_quo 2 integer quotient
|
|
xx_mod 2 remainder of division
|
|
xx_int 1 integer part
|
|
xx_frac 1 fractional part
|
|
xx_inc 1 increment, default adds 1
|
|
xx_dec 1 decrement, default subtracts 1
|
|
xx_square 1 default multiplies by itself
|
|
xx_scale 2 multiply by power of 2
|
|
xx_shift 2 shift left by n bits (right if negative)
|
|
xx_round 2 round to given number of decimal places
|
|
xx_bround 2 round to given number of binary places
|
|
xx_root 3 root of value within given error
|
|
xx_sqrt 2 square root within given error
|
|
|
|
|
|
Also see the library files:
|
|
|
|
dms.cal
|
|
mod.cal
|
|
poly.cal
|
|
quat.cal
|
|
surd.cal
|