Commit | Line | Data |
---|---|---|
e1a1d2b4 C |
1 | Using objects |
2 | ||
3 | Objects are user-defined types which are associated with user- | |
4 | defined functions to manipulate them. Object types are defined | |
5 | similarly to structures in C, and consist of one or more elements. | |
6 | The advantage of an object is that the user-defined routines are | |
7 | automatically called by the calculator for various operations, | |
8 | such as addition, multiplication, and printing. Thus they can be | |
9 | manipulated by the user as if they were just another kind of number. | |
10 | ||
11 | An example object type is "surd", which represents numbers of the form | |
12 | ||
13 | a + b*sqrt(D), | |
14 | ||
15 | where D is a fixed integer, and 'a' and 'b' are arbitrary rational | |
16 | numbers. Addition, subtraction, multiplication, and division can be | |
17 | performed on such numbers, and the result can be put unambiguously | |
18 | into the same form. (Complex numbers are an example of surds, where | |
19 | D is -1.) | |
20 | ||
21 | The "obj" statement defines either an object type or an actual | |
22 | variable of that type. When defining the object type, the names of | |
23 | its elements are specified inside of a pair of braces. To define | |
24 | the surd object type, the following could be used: | |
25 | ||
26 | obj surd {a, b}; | |
27 | ||
28 | Here a and b are the element names for the two components of the | |
29 | surd object. An object type can be defined more than once as long | |
30 | as the number of elements and their names are the same. | |
31 | ||
32 | When an object is created, the elements are all defined with zero | |
33 | values. A user-defined routine should be provided which will place | |
34 | useful values in the elements. For example, for an object of type | |
35 | 'surd', a function called 'surd' can be defined to set the two | |
36 | components as follows: | |
37 | ||
38 | define surd(a, b) | |
39 | { | |
40 | local x; | |
41 | ||
42 | obj surd x; | |
43 | x.a = a; | |
44 | x.b = b; | |
45 | return x; | |
46 | } | |
47 | ||
48 | When an operation is attempted for an object, user functions with | |
49 | particular names are automatically called to perform the operation. | |
50 | These names are created by concatenating the object type name and | |
51 | the operation name together with an underscore. For example, when | |
52 | multiplying two objects of type surd, the function "surd_mul" is | |
53 | called. | |
54 | ||
55 | The user function is called with the necessary arguments for that | |
56 | operation. For example, for "surd_mul", there are two arguments, | |
57 | which are the two numbers. The order of the arguments is always | |
58 | the order of the binary operands. If only one of the operands to | |
59 | a binary operator is an object, then the user function for that | |
60 | object type is still called. If the two operands are of different | |
61 | object types, then the user function that is called is the one for | |
62 | the first operand. | |
63 | ||
64 | The above rules mean that for full generality, user functions | |
65 | should detect that one of their arguments is not of its own object | |
66 | type by using the 'istype' function, and then handle these cases | |
67 | specially. In this way, users can mix normal numbers with object | |
68 | types. (Functions which only have one operand don't have to worry | |
69 | about this.) The following example of "surd_mul" demonstrates how | |
70 | to handle regular numbers when used together with surds: | |
71 | ||
72 | define surd_mul(a, b) | |
73 | { | |
74 | local x; | |
75 | ||
76 | obj surd x; | |
77 | if (!istype(a, x)) { | |
78 | /* a not of type surd */ | |
79 | x.a = b.a * a; | |
80 | x.b = b.b * a; | |
81 | } else if (!istype(b, x)) { | |
82 | /* b not of type surd */ | |
83 | x.a = a.a * b; | |
84 | x.b = a.b * b; | |
85 | } else { | |
86 | /* both are surds */ | |
87 | x.a = a.a * b.a + D * a.b * b.b; | |
88 | x.b = a.a * b.b + a.b * b.a; | |
89 | } | |
90 | if (x.b == 0) | |
91 | return x.a; /* normal number */ | |
92 | return x; /* return surd */ | |
93 | } | |
94 | ||
95 | In order to print the value of an object nicely, a user defined | |
96 | routine can be provided. For small amounts of output, the print | |
97 | routine should not print a newline. Also, it is most convenient | |
98 | if the printed object looks like the call to the creation routine. | |
99 | For output to be correctly collected within nested output calls, | |
100 | output should only go to stdout. This means use the 'print' | |
101 | statement, the 'printf' function, or the 'fprintf' function with | |
102 | 'files(1)' as the output file. For example, for the "surd" object: | |
103 | ||
104 | define surd_print(a) | |
105 | { | |
106 | print "surd(" : a.a : "," : a.b : ")" : ; | |
107 | } | |
108 | ||
109 | It is not necessary to provide routines for all possible operations | |
110 | for an object, if those operations can be defaulted or do not make | |
111 | sense for the object. The calculator will attempt meaningful | |
112 | defaults for many operations if they are not defined. For example, | |
113 | if 'surd_square' is not defined to square a number, then 'surd_mul' | |
114 | will be called to perform the squaring. When a default is not | |
115 | possible, then an error will be generated. | |
116 | ||
117 | Please note: Arguments to object functions are always passed by | |
118 | reference (as if an '&' was specified for each variable in the call). | |
119 | Therefore, the function should not modify the parameters, but should | |
120 | copy them into local variables before modifying them. This is done | |
121 | in order to make object calls quicker in general. | |
122 | ||
123 | The double-bracket operator can be used to reference the elements | |
124 | of any object in a generic manner. When this is done, index 0 | |
125 | corresponds to the first element name, index 1 to the second name, | |
126 | and so on. The 'size' function will return the number of elements | |
127 | in an object. | |
128 | ||
129 | The following is a list of the operations possible for objects. | |
130 | The 'xx' in each function name is replaced with the actual object | |
131 | type name. This table is displayed by the 'show objfuncs' command. | |
132 | ||
133 | Name Args Comments | |
134 | ||
135 | xx_print 1 print value, default prints elements | |
136 | xx_one 1 multiplicative identity, default is 1 | |
137 | xx_test 1 logical test (false,true => 0,1), | |
138 | default tests elements | |
139 | xx_add 2 | |
140 | xx_sub 2 subtraction, default adds negative | |
141 | xx_neg 1 negative | |
142 | xx_mul 2 | |
143 | xx_div 2 non-integral division, default multiplies | |
144 | by inverse | |
145 | xx_inv 1 multiplicative inverse | |
146 | xx_abs 2 absolute value within given error | |
147 | xx_norm 1 square of absolute value | |
148 | xx_conj 1 conjugate | |
149 | xx_pow 2 integer power, default does multiply, | |
150 | square, inverse | |
151 | xx_sgn 1 sign of value (-1, 0, 1) | |
152 | xx_cmp 2 equality (equal,non-equal => 0,1), | |
153 | default tests elements | |
154 | xx_rel 2 inequality (less,equal,greater => -1,0,1) | |
155 | xx_quo 2 integer quotient | |
156 | xx_mod 2 remainder of division | |
157 | xx_int 1 integer part | |
158 | xx_frac 1 fractional part | |
159 | xx_inc 1 increment, default adds 1 | |
160 | xx_dec 1 decrement, default subtracts 1 | |
161 | xx_square 1 default multiplies by itself | |
162 | xx_scale 2 multiply by power of 2 | |
163 | xx_shift 2 shift left by n bits (right if negative) | |
164 | xx_round 2 round to given number of decimal places | |
165 | xx_bround 2 round to given number of binary places | |
166 | xx_root 3 root of value within given error | |
167 | xx_sqrt 2 square root within given error | |
168 | ||
169 | ||
170 | Also see the library files: | |
171 | ||
172 | dms.cal | |
173 | mod.cal | |
174 | poly.cal | |
175 | quat.cal | |
176 | surd.cal |