Lesson_4.red 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. COMMENT
  2. REDUCE INTERACTIVE LESSON NUMBER 4
  3. David R. Stoutemyer
  4. University of Hawaii
  5. COMMENT This is lesson 4 of 7 REDUCE lessons. As before, please
  6. refrain from using variables beginning with the letters F through H
  7. during the lesson.
  8. In theory, assignments and LET statements are sufficient to accomplish
  9. anything that any other practical computing mechanism is capable of
  10. doing. However, it is more convenient for some purposes to use
  11. function procedures which can employ branch selection and iteration as
  12. do most traditional programming languages. As a trivial example, if
  13. we invariably wanted to replace cotangents with the corresponding
  14. tangents, we could input:;
  15. algebraic procedure cotan(x); 1/tan(x);
  16. COMMENT As an example of the use of this function, we have;
  17. cotan(log(f));
  18. pause;
  19. COMMENT Note:
  20. 1. The procedure definition automatically declares the procedure
  21. name as an operator.
  22. 2. A procedure can be executed any time after its definition,
  23. until it is cleared.
  24. 3. Any parameters are dummy variables that are distinct from any
  25. other variables with the same name outside the procedure
  26. definition, and the corresponding arguments can be arbitrary
  27. expressions.
  28. 4. The value returned by a procedure is the value of the
  29. expression following the procedure statement.
  30. 5. The function COT is already defined in REDUCE and should not be
  31. redefined.
  32. We can replace this definition with a different one:;
  33. algebraic procedure cotan(y); cos(y)/sin(y);
  34. g1 := cotan(log(f));
  35. COMMENT In place of the word ALGEBRAIC, we can optionally use the word
  36. INTEGER when a function always returns an integer value, or we can
  37. optionally use the word REAL when a function always returns a
  38. floating-point value. (ALGEBRAIC can also be omitted, since it is the
  39. default procedure type.)
  40. Try writing a procedure definition for the sine in terms of the
  41. cosine, then type G1.;
  42. pause;
  43. COMMENT Here is a more complicated function which introduces the
  44. notion of a conditional expression:;
  45. algebraic procedure sumcheck(aj, j, m, n, s);
  46. COMMENT J is an indeterminate and the other parameters are
  47. expressions. This function returns the global variable named
  48. PROVED if the function can inductively verify that S equals the
  49. sum of AJ for J going from M through N, returning the global
  50. variable named UNPROVED otherwise. For the best chance of
  51. proving a correct sum, the function should be executed under the
  52. influence of ON EXP, ON MCD, and any other user-supplied
  53. simplification rules relevant to the expression classes of AJ
  54. and S;
  55. if sub(j=m,aj) - sub(n=m,s) neq 0 or
  56. s + sub(j=n+1,aj) - sub(n=n+1,s) neq 0 then unproved
  57. else proved;
  58. on exp, mcd;
  59. clear x, j, n;
  60. sumcheck(j, j, 1, n, n*(n+1)/2);
  61. sumcheck(x^j, j, 0, n, (x^(n+1)-1)/(x-1));
  62. COMMENT Within procedures of this sort a global variable is any
  63. variable which is not one of the parameters, and a global variable has
  64. the value, if any, which is current for that name at the point from
  65. where the procedure is used.;
  66. pause;
  67. COMMENT Conditional expressions have the form
  68. IF condition THEN expression1 ELSE expression2.
  69. There are generally several equivalent ways of writing a conditional
  70. expression. For example, the body of the above procedure could have
  71. been written
  72. IF SUB(J=M,AJ) - SUB(N=M,S) = 0 AND
  73. S + SUB(J=N+1,AJ) - SUB(N=N+1,S) = 0 THEN PROVED
  74. ELSE UNPROVED.
  75. Note how we compare a difference with 0, rather than comparing two
  76. nonzero expressions, for reasons explained in lesson 3.
  77. As an exercise, write a procedure analogous to SUMCHECK for proving
  78. closed-form product formulas, then test it on the valid formula that
  79. COS(N*X) equals the product of COS(J*X)/COS(J*X-X) for J ranging from
  80. 1 through N. You do not need to include prefatory comments describing
  81. parameters and the returned value until you learn how to use a text
  82. editor.;
  83. pause;
  84. COMMENT Most REDUCE statements are also expressions because they have
  85. a value. The value is usually 0 if nothing else makes sense, but I
  86. will mention the value only if it is useful.
  87. The value of an assignment statement is the assigned value. Thus a
  88. multiple assignment, performed right to left, can be achieved by a
  89. sequence of the form
  90. variable1 := variable2 := ... := variableN := expression.
  91. Moreover, assignments can be inserted within ordinary expressions such
  92. as X*(Y:=5). Such assignments must usually be parenthesized because
  93. of the low precedence of the assignment operator, and excessive use of
  94. this construct tends to make programs confusing.;
  95. pause;
  96. COMMENT REDUCE treats as a single expression any sequence of
  97. statements preceded by the pair of adjacent characters << and followed
  98. by the pair >>. The value of such a group expression is the value of
  99. the last statement in the group.
  100. Group expressions facilitate the implementation of tasks that are most
  101. easily stated as a sequence of operations. However, such sequences
  102. often utilize temporary variables to count, hold intermediate results,
  103. etc., and it is hazardous to use global variables for that purpose.
  104. If a top-level REDUCE statement or another function directly or
  105. indirectly uses that variable name, then its value or its virgin
  106. indeterminate status there might be damaged by our use as a temporary
  107. variable. In large programs or programs which rely on the work of
  108. others, such interference has a non-negligible probability, even if
  109. all programmers agree to the convention that all such temporary
  110. variables should begin with the function name as a prefix and all
  111. programmers attempt to comply with the convention. For this reason,
  112. REDUCE provides another expression-valued sequence called a
  113. BEGIN-block, which permits the declaration of local variables that are
  114. distinct from any other variables outside the block having the same
  115. name. Another advantage of using local variables for temporary
  116. variables is that the perhaps large amount of storage occupied by
  117. their values can be reclaimed after leaving their block.;
  118. pause;
  119. COMMENT A BEGIN-block consists of the word BEGIN, followed by optional
  120. declarations, followed by a sequence of statements, followed by the
  121. word END. Within BEGIN-blocks, it is often convenient to return
  122. control and possibly a value from someplace other than the end of the
  123. block. Control and a value may be returned via a RETURN-statement of
  124. the form
  125. RETURN expression
  126. or
  127. RETURN,
  128. 0 being returned in the latter case. A BEGIN-block does not return
  129. the value of the last statement. If a value is to be returned then
  130. RETURN must be used. These features and others are illustrated by the
  131. following function:;
  132. pause;
  133. algebraic procedure limit(ex, indet, pnt);
  134. begin COMMENT This function uses up through 4 iterations of L'Hospital's
  135. rule to attempt determination of the limit of expression EX as
  136. indeterminate INDET approaches expression PNT. This function is
  137. intended for the case where SUB(INDET=PNT, EX) yields 0/0,
  138. provoking a zero-divide message. This function returns the
  139. global variable named UNDEFINED when the limit is 0 dividing an
  140. expression which did not simplify to 0, and this function
  141. returns the global variable named UNKNOWN when it cannot
  142. determine the limit. Otherwise this function returns an
  143. expression which is the limit. For best results, this function
  144. should be executed under the influence of ON EXP, ON MCD, and
  145. any user-supplied simplification rules appropriate to the
  146. expression classes of EX and PNT;
  147. integer iteration;
  148. scalar n, d, nlim, dlim;
  149. iteration := 0;
  150. n := num(ex);
  151. d := den(ex);
  152. nlim := sub(indet=pnt, n);
  153. dlim := sub(indet=pnt, d);
  154. while nlim=0 and dlim=0 and iteration<5 do <<
  155. n := df(n, indet);
  156. d := df(d, indet);
  157. nlim := sub(indet=pnt, n);
  158. dlim := sub(indet=pnt, d);
  159. iteration := iteration + 1 >>;
  160. return (if nlim=0 then
  161. if dlim=0 then unknown
  162. else 0
  163. else if dlim=0 then undefined
  164. else nlim/dlim)
  165. end;
  166. % Examples follow...
  167. pause;
  168. g1 := (e^x-1)/x;
  169. % Evaluation at 0 causes a zero denominator error at top level but
  170. % continue anyway.
  171. sub(x=0, g1);
  172. limit(g1, x, 0);
  173. g1:= ((1-x)/log(x))^2;
  174. % Evaluation at 1 causes a zero denominator error at top level but
  175. % continue anyway.
  176. sub(x=1, g1);
  177. limit(g1, x, 1);
  178. COMMENT Note:
  179. 1. The idea behind L'Hospital's rule is that as long as the
  180. numerator and denominator are both zero at the limit point, we
  181. can replace them by their derivatives without altering the
  182. limit of the quotient.
  183. 2. Assignments within groups and BEGIN-blocks do not automatically
  184. cause output.
  185. 3. Local variables are declared INTEGER, REAL, or SCALAR, the
  186. latter corresponding to the same most general class denoted by
  187. ALGEBRAIC in a procedure statement. All local variables are
  188. initialized to zero, so they cannot serve as indeterminates.
  189. Moreover, if we attempted to overcome this by clearing them, we
  190. would clear all variables with their names.
  191. 4. We do not declare the attributes of parameters.
  192. 5. The NUM and DEN functions respectively extract the numerator
  193. and denominator of their arguments. (With OFF MCD, the
  194. denominator of 1+1/X would be 1.)
  195. 6. The WHILE-loop has the general form
  196. WHILE condition DO statement.
  197. REDUCE also has a "GO TO" statement, and using commas rather
  198. than semicolons to prevent termination of this comment, the
  199. above general form of a WHILE-loop is equivalent to
  200. BEGIN GO TO TEST,
  201. LOOP: statement,
  202. TEST: IF condition THEN GO TO LOOP,
  203. RETURN 0
  204. END.
  205. A GOTO statement is permitted only within a block, and the GOTO
  206. statement cannot refer to a label outside the same block or to
  207. a label inside a block that the GOTO statement is not also
  208. within. Actually, 99.99% of REDUCE BEGIN-blocks are less
  209. confusing if written entirely without GOTOs, and I mention them
  210. primarily to explain WHILE-loops in terms of a more primitive
  211. notion.;
  212. pause;
  213. COMMENT
  214. 7. The LIMIT function provides a good illustration of nested
  215. conditional expressions. Proceeding sequentially through such
  216. nests, each ELSE clause is matched with the nearest preceding
  217. unmatched THEN clause in the group or block. In order to help
  218. reveal their structure, I have consistently indented nested
  219. conditional statements, continuations of multi-line statements
  220. and loop-bodies according to one of the many staunchly defended
  221. indentation styles. (If you have an instructor, I also urge
  222. you to humor him by adopting his style for the duration of the
  223. course.)
  224. 8. C and Java programmers take note: "IF ... THEN ... ELSE ..." is
  225. regarded as one expression, and semicolons are used to separate
  226. rather than terminate statements. Moreover, BEGIN and END are
  227. brackets rather than statements, so a semicolon is never needed
  228. immediately after BEGIN, and a semicolon is necessary
  229. immediately preceding END only if the END is intended as a
  230. labeled destination for a GOTO. Within conditional
  231. expressions, an inappropriate semicolon after an END, a >>, or
  232. an ELSE-clause is likely to be one of your most prevalent
  233. mistakes.;
  234. pause;
  235. COMMENT The next exercise is based on the above LIMIT function:
  236. For the sum of positive expressions AJ for J ranging from some finite
  237. initial value to infinity, the infinite series converges if the limit
  238. of the ratio SUB(J=J+1,AJ)/AJ is less than 1 as J approaches infinity.
  239. The series diverges if this limit exceeds 1, and the test is
  240. inconclusive if the limit is 1. To convert the problem to the form
  241. required by the above LIMIT program, we can replace J by 1/!*FOO in
  242. the ratio, then take the limit as the indeterminate !*FOO approaches
  243. zero. (Since an indeterminate is necessary here, I picked the weird
  244. name !*FOO to make the chance of conflict negligible.)
  245. After writing such a function to perform the ratio test, test it on
  246. the examples AJ=J/2^J, AJ=1/J^2, AJ=2^J/J^10, and AJ=1/J. (The first
  247. two converge and the second two diverge.);
  248. pause;
  249. COMMENT Groups or blocks can be used wherever any arbitrary expression
  250. is allowed, including the right-hand side of a LET rule.
  251. The need for loops with an integer index variable running from a given
  252. initial value through a given final value by a given increment is so
  253. prevalent that REDUCE offers a convenient special way of accomplishing
  254. it via a FOR-loop, which has the general form
  255. FOR index := initial STEP increment UNTIL final DO statement.
  256. Except for the use of commas as statement separators, this construct
  257. is equivalent to
  258. BEGIN INTEGER index,
  259. index := initial,
  260. IF increment>0 THEN WHILE index <= final DO <<
  261. statement,
  262. index := index + increment >>
  263. ELSE WHILE index >= final DO <<
  264. statement,
  265. index := index + increment >>,
  266. RETURN 0
  267. END;
  268. pause;
  269. COMMENT Note:
  270. 1. The index variable is automatically declared local to the FOR-
  271. loop.
  272. 2. "initial", "increment", and "final" must have integer values.
  273. 3. FORTRAN programmers take note: the body of the loop is not
  274. automatically executed at least once.
  275. 4. An abbreviation for "STEP 1 UNTIL" is ":".
  276. 5. Since the WHILE-loop and the FOR-loop have implied BEGIN-
  277. blocks, a RETURN statement within their bodies cannot transfer
  278. control further than the point following the loops.
  279. Another frequent need is to produce output from within a group or
  280. block, because such output is not automatically produced. This can be
  281. done using the WRITE-statement, which has the form
  282. WRITE expression1, expression2, ..., expressionN.
  283. Beginning a new line with expression1, the expressions are printed
  284. immediately adjacent to each other, split over line boundaries if
  285. necessary. The value of the WRITE-statement is the value of its last
  286. expression, and any of the expressions can be a character-string of
  287. the form "character1 character2 ... characterM".
  288. Inserting the word "WRITE" on a separate line before an assignment is
  289. convenient for debugging, because the word is then easily deleted
  290. afterward. These features and others are illustrated by the following
  291. equation solver:;
  292. pause;
  293. operator solvefor, soln;
  294. for all x, lhs, rhs let solvefor(x, lhs, rhs) = solvefor(x, lhs-rhs);
  295. COMMENT LHS and RHS are expressions such that P=NUM(LHS-RHS) is a
  296. polynomial of degree at most 2 in the indeterminate or functional form
  297. X. Otherwise an error message is printed. As a convenience, RHS can
  298. be omitted if it is 0. If P is quadratic in X, the two values of X
  299. which satisfy P=0 are stored as the values of the functional forms
  300. SOLN(1) and SOLN(2). If P is a first-degree polynomial in X, SOLN(1)
  301. is set to the one solution. If P simplifies to 0, SOLN(1) is set to
  302. the identifier ARBITRARY. If P is an expression which does not
  303. simplify to zero but does not contain X, SOLN(1) is set to the
  304. identifier NONE. In all other cases, SOLN(1) is set to the identifier
  305. UNKNOWN. The function then returns the number of SOLN forms which
  306. were set. This function prints a well deserved warning message if the
  307. denominator of LHS-RHS contains X. If LHS-RHS is not polynomial in X,
  308. it is wise to execute this function under the influence of ON GCD.;
  309. pause;
  310. for all x, lhsmrhs let solvefor(x, lhsmrhs) =
  311. begin integer hipow; scalar temp, cflist, cf0, cf1, cf2;
  312. if lhsmrhs = 0 then <<
  313. soln(1) := arbitrary;
  314. return 1 >>;
  315. cflist := coeff(lhsmrhs, x);
  316. hipow := hipow!*;
  317. if hipow = 0 then <<
  318. soln(1) := none;
  319. return 1 >>;
  320. if hipow > 2 then <<
  321. soln(1) := unknown;
  322. return 1 >>;
  323. if hipow = 1 then <<
  324. soln(1) := first(cflist)/second(cflist);
  325. if df(sub(x=!*foo, soln(1)), !*foo) neq 0 then
  326. soln(1) := unknown;
  327. return 1 >>;
  328. cf0 := first(cflist)/third(cflist);
  329. cf1 := -second(cflist)/third(cflist)/2;
  330. if df(sub(x=!*foo, cf0), !*foo) neq 0
  331. or df(sub(x=!*foo, cf1), !*foo) neq 0 then <<
  332. soln(1) := unknown;
  333. return 1 >>;
  334. temp := (cf1^2 - cf0)^(1/2);
  335. soln(1) := cf1 + temp;
  336. soln(2) := cf1 - temp;
  337. return 2
  338. end;
  339. COMMENT And some examples:;
  340. pause;
  341. for k:=1:solvefor(x, a*x^2, -b*x-c) do write soln(k) := soln(k);
  342. for k:=1:solvefor(log(x), 5*log(x)-7) do write soln(k) := soln(k);
  343. for k:=1:solvefor(x, x, x) do write soln(k) := soln(k);
  344. for k:= 1:solvefor(x, 5) do write soln(k) := soln(k);
  345. for k:=1:solvefor(x, x^3+x+1) do write soln(k) := soln(k);
  346. for k:=1:solvefor(x, x*e^x, 1) do write soln(k) := soln(k);
  347. g1 := x/(e^x-1);
  348. % Results in 'invalid as polynomial' error, continue anyway:;
  349. for k:=1:solvefor(x, g1) do write soln(k) := soln(k);
  350. sub(x=soln(1), g1);
  351. limit(g1, x, soln(1));
  352. pause;
  353. COMMENT Here we have used LET rules to permit the user the convenience
  354. of omitting default arguments. (Function definitions have to have a
  355. fixed number of parameters.)
  356. Array elements are designated by the same syntax as matrix elements,
  357. namely as functional forms having integer arguments. Here are some
  358. desiderata that may help you decide which of these alternatives is
  359. most appropriate for a particular application:
  360. 1. The lower bound of each array subscript is 0 vs. 1 for
  361. matrices vs. unrestricted for functional forms.
  362. 2. The upper bound of each array subscript must have a specific
  363. integer value at the time the array is declared, as must the
  364. upper bounds of matrix subscripts when a matrix is first
  365. referred to, on the left side of a matrix assignment. In
  366. contrast, functional forms never require a commitment to a
  367. specific upper bound.
  368. 3. An array can have any fixed number of subscripts, a matrix must
  369. have exactly 2, and a functional form can have a varying
  370. arbitrary number.
  371. 4. Matrix operations, such as transpose and inverse, are built-in
  372. only for matrices.
  373. 5. For most implementations, access to array elements requires
  374. time approximately proportional to the number of subscripts,
  375. whereas access to matrix elements takes time approximately
  376. proportional to the sum of the two subscript values, whereas
  377. access to functional forms takes average time approximately
  378. proportional to the number of bound functional forms having
  379. that name.
  380. 6. Only functional forms permit the effect of a subscripted
  381. indeterminate such as having an answer be "A(M,N) + B(3,4)".
  382. 7. Only functional forms can be used alone in the LHS of LET
  383. substitutions.;
  384. pause;
  385. COMMENT
  386. 8. All arrays, matrices, and operators are global regardless of
  387. where they are declared, so declaring them within a BEGIN-block
  388. does not afford the protection and automatic storage recovery
  389. of local variables. Moreover, clearing them within a
  390. BEGIN-block will clear them globally, and normal functions
  391. cannot return an array or a matrix value. Furthermore, REDUCE
  392. parameters are referenced by value, which means that an
  393. assignment to a parameter has no effect on the corresponding
  394. argument. Thus, matrix or array results cannot be transmitted
  395. back to an argument either.
  396. 9. It is often advantageous to use two or more of these
  397. alternatives to represent a set of quantities at different
  398. times in the same program. For example, to get the general
  399. form of the inverse of a 3-by-3 matrix, we could write
  400. MATRIX AA(3,3),
  401. OPERATOR A,
  402. FOR J:=1:3 DO
  403. FOR K:=1:3 DO AA(J,K) := A(J,K),
  404. AA^-1.
  405. As another example, we might use an array to receive some
  406. polynomial coefficients, then transfer the values to a matrix
  407. for inversion.;
  408. pause;
  409. COMMENT The COEFF function is the remaining new feature in our
  410. SOLVEFOR example. The first argument is a polynomial expression in
  411. the indeterminate or functional form which is the second argument.
  412. The polynomial coefficients of the integer powers of the indeterminate
  413. are returned as a LIST, with the independent coefficient first. The
  414. highest and lowest non-zero powers are placed in the variables HIPOW!*
  415. and LOWPOW!* respectively.
  416. A LIST is a kind of data structure, just as matrices and arrays are.
  417. It is represented as a comma-separated sequence of elements enclosed
  418. in braces. The elements can be accessed with the functions FIRST,
  419. SECOND, THIRD, PART(i) which returns the i-th element, and REST, which
  420. returns a list of all but the first element. For example:;
  421. clear x;
  422. coeff(x^5+2, x);
  423. lowpow!*;
  424. hipow!*;
  425. pause;
  426. COMMENT COEFF does not check to make sure that the coefficients do not
  427. contain its second argument within a functional form, so that is the
  428. reason we differentiated. The reason we first substituted the
  429. indeterminate !*FOO for the second argument is that differentiation
  430. does not work with respect to a functional form.
  431. The last exercise is to rewrite the last rule so that we can solve
  432. equations which simplify to the form
  433. a*x^(m+2*l) + b*x^(m+l) + c*x^m = 0, where m >= 0 and l >= 1.
  434. The solutions are
  435. 0, with multiplicity m,
  436. x1*E^(2*j*I*pi/l),
  437. x2*E^(2*j*I*pi/l), with j = 0, 1, ..., l-1,
  438. where x1 and x2 are the solutions to the quadratic equation
  439. a*x^2 + b*x + c = 0.
  440. As a convenience to the user, you might also wish to have a global
  441. switch named SOLVEPRINT, such that when it is nonzero, the solutions
  442. are automatically printed.
  443. This is the end of lesson 4. When you are ready to run lesson 5,
  444. start a new REDUCE session.
  445. ;end;