MathClass Lesson 1:
Blocks and Bricks
Getting Maple to do Repetitive Tasks
Expression Sequences
The process of analyzing or solving a problem can involve a lot of repetition. Anyone who has graphed many different functions knows the tedious repetition of that process. Maple's built-in "plot" commands allow the computer do the mindless repetition, freeing time for people to look at the relationships implied by the graph - something the machine can't do. Graphing, however is only one example of this of this capability which is absolutely critical to effective use of the computer for solving general problems. Lets look at another - building lists.
We have already made and used a lot of Maple lists. Recall that to Maple a list is simply a construct of the form
where a,b, etc can be anything at all. [joe, sam, #, 1, @,
]
is a perfectly good Maple list.
A more primitive notion than that of a list is that of an
expression sequence.
For our immediate purposes the most important thing about expression sequences is how easy it is to extend them. If "Joe" is the expression sequence "Sam,Bill" and we want to add "Hortense" to the end of "joe" we can simply enter
> Joe:=Sam,Bill;
> Joe:=Joe, Hortense;
>
This has redefined "Joe"
If we wanted to place Mary at the beginning we would enter "Joe:=Mary,Joe;"
This idea also allows one to join two sequences. If "Joe" and "Harry" are sequences then "Joe,Harry" is another which starts with the elements of "Joe" in the order "Joe" has them and continues with those of "Harry" in the order "Harry" has them
.
NULL, the empty sequence
We will need to construct many sequences, often starting out with no knowledge of even the first element. In such cases it is very useful to have an empty expression (i.e. an expression but has no items in it) to use to get started. This is evidently analogous to the empty set in set theory. The concept is so useful that Maple has a special name for the empty expression sequence. It is called "NULL".
With the notion of expression sequence we can observe that a list is simply an expression sequence delimited by brackets "[ ]". Most important, to convert an expression sequence, "joe, " to a list "sam" we simply enclose it in brackets "sam:=[joe];". Finally, since a list is simply an expression sequence enclosed in brackets there is a way to extract the expression sequence from a list using a command called "op" which we will see often.
First lets make a simple sequence. We will start with the NULL sequence to indicate how it is used as a starting point for building sequences
> joe:=NULL;
> harry:=joe,Sally,1;
> whattype(joe);
> whattype(harry);
> joe:= harry,dog, Pi;
> whattype(joe);
> sam:=[joe];
> whattype(sam);
> tim:=op(sam);
> whattype(tim);
Now suppose we want to make the list "stuff:= [sally,1,dog,
,
Lance]". We observe that we should be able to do this simply by adding "Lance" to the end of the list "sam". We would like to do something like "stuff:=sam,Lance;" or "stuff:=[sam,Lance];" but neither of these will work. This is not to say that Maple won't accept the commands
> stuff:=sam,Lance;
> whattype(stuff);
> stuff:=[sam,Lance];
Neither of these works. The first produces an expression sequence, not a list. The second does produce a list but it is a list with two elements: the first element is the list "
[s
ally,1,dog
,
]"
and the second is "Lance".
In order to produce the list we want we have to extract the expression sequence from "sam", append "Lance " to it and then convert that sequence to a list.
> tmp:=op(sam);
> tmp:=tmp,Lance;
> stuff:=[tmp];
> whattype(stuff);
We can be more economical by combining steps by composing commands:
> stuff:=[op(sam),Lance];
Incidentally, to go with "op" there is a very useful commnad "nops" (number of operands) which we can use to tell us how many items there are in a list
> nops(stuff);
Note that nops doesn't work on an expression sequence. Thus in order to count the number of items in an expression sequence we must ask nops to act on a list of which the expression sequence is the operand. To do this it is not necessary to actually convert the sequence into a list and then convert it back.
> nops(tmp);
Error, wrong number (or type) of parameters in function nops
> nops([tmp]);
It is important to note that writing "[tmp]" in this command does not change "tmp" into a list. The assignment command "tmp:=[tmp];" would do that but we have not assigned a new value to "tmp". All we have done is make an "unnamed" copy of the list "[tmp]" and have "nops" act on it. This leads to a comment we must make regarding what happens when we make an expression sequence some of whose elements are objects that have assigned values. For instance suppose we have executed "bird:=turkey;" and subsequently make an expressions sequence "animals:=cat,dog,bird;" The sequence "animals" will be "cat,dog,turkey", that is the value that has been assigned to any of the terms in the sequence we write down will be substituted immediately. If we then redefine "bird", say "bird:=pigeon;" the sequence "animals" will still be "cat,dog,turkey" unless we re-execute the command "animals:=cat,dog,bird;" at which time "animals" will become "cat,dog,pigeon".
> bird:=turkey;
> bird;
> animals:=cat,dog,bird;
> animals;
> bird:=pigeon;
> bird;
> animals;
> animals:=cat,dog,bird;
> animals;
This property of sequences is extremely important for it allows us to build a sequence in steps re-using a temporary name for the item we are adding.
> dogs:=NULL;
> tmpdog:=Beagle;
> dogs:=dogs,tmpdog;
> tmpdog:=Airdale;
> dogs:=dogs,tmpdog;
Automatically Generating Sequences and Lists
Now suppose we wanted to make a list of the first 25 positive intgers. One way to do this is simply to enter it:
> numlist:=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,17,18,19,20,21,22,23,24,24,25];
This is tedious and fraught with opportunity to make mistakes. Indeed the careful reader will have noted that this isn't the correct list, "17" was entered twice. Even that careful soul might not take the time to check whether this sequence which clams to list the numbers from 672345 to 677345 is actually correct.
> boringlist := [6 72345, 672346, 672347, 672348, 672349, 672350, 672351, 672352, 672353, 672354, 672355, 672356, 672357, 672358, 672359, 672360, 672361, 672362, 672363, 672364, 672365, 672366, 672367, 672368, 672369, 672370, 672371, 672372, 672373, 672374, 672375, 672376, 672377, 672378, 672379, 672 380, 672381, 672382, 672383, 672384, 672385, 672386, 672387, 672388, 672389, 672390, 672391, 672392, 672393, 672394, 672395, 672396, 672397, 672398, 672399, 672400, 672401, 672402, 672403, 672404, 672405, 672406, 672407, 672408, 672409, 672410, 672411, 672412, 672413, 672414, 672415, 672416, 672417, 672418, 672419, 672420, 672421, 672422, 672423, 672424, 672425, 672426, 672427, 672428, 672429, 672430, 672431, 672432, 672433, 672434, 672435, 672436, 672437, 672438, 672439, 672440, 672441, 672442, 672443, 672444, 672445, 672446, 672447, 672448, 672449, 672450, 672451, 672452, 672453, 672454, 672455, 672456, 672457, 672458, 672459, 672460, 672461, 672462, 672463, 672464, 672465, 672466, 672467, 672468, 672469, 672470, 672471, 672472, 672473, 672474, 672475, 672476, 672477, 672478, 672479, 672480, 672481, 672482, 672483, 672484, 672485, 672486, 672487, 672488, 672489, 672490, 672491, 672492, 672493, 672494, 672495, 672496, 672497, 672498, 672499, 672500, 672501, 672502, 672503, 672504, 672505, 672506, 672507, 672508, 672509, 672510, 672511, 672512, 672513, 672514, 672515, 672516, 672517, 672518, 672519, 672520, 672521, 672522, 672523, 672524, 672525, 672526, 672527, 672528, 672529, 672530, 672531, 672532, 672533, 672534, 672535, 672536, 672537, 672538, 672539, 672540, 672541, 672542, 672543, 672544, 672545, 672546, 672547, 672548, 672549, 672550, 672551, 672552, 672553, 672554, 672555, 672556, 672557, 672558, 672559, 672560, 672561, 672562, 672563, 672564, 672565, 672566, 672567, 672568, 672569, 672570, 672571, 672572, 672573, 672574, 672575, 672576, 672577, 672578, 672579, 672580, 672581, 672582, 672583, 672584, 672585, 672586, 672587, 672588, 672589, 672590, 672591, 672592, 672593, 672594, 672595, 672596, 672597, 672598, 672599, 672600, 672601, 672602, 672603, 672604, 672605, 672606, 672607, 672608, 672609, 672610, 672611, 672612, 672613, 672614, 672615, 672616, 672617, 672618, 672619, 672620, 672621, 672622, 672623, 672624, 672625, 672626, 672627, 672628, 672629, 672630, 672631, 672632, 672633, 672634, 672635, 672636, 672637, 672638, 672639, 672640, 672641, 672642, 672643, 672644, 672645, 672646, 672647, 672648, 672649, 672650, 672651, 672652, 672653, 672654, 672655, 672656, 672657, 672658, 672659, 672660, 672661, 672662, 672663, 672664, 672665, 672666, 672667, 672668, 672669, 672670, 672671, 672672, 672673, 672674, 672675, 672676, 672677, 672678, 672679, 672680, 672681, 672682, 672683, 672684, 672685, 672686, 672687, 672688, 672689, 672690, 672691, 672692, 672693, 672694, 672695, 672696, 672697, 672698, 672699, 672700, 672701, 672702, 672703, 672704, 672705, 672706, 672707, 672708, 672709, 672710, 672711, 672712, 672713, 672714]
> boringlist:=[seq(i,i=672345..672714)];
Note that "seq" returns an expression sequence. The brackets in turn make that a list. An expression sequence of the cubes of the integers from -3 to 13 is simply
> cubesequence:=seq(i^3,i=-3..13);
We can convert it into a list
> cubelist:=[cubesequence];
i f the list was all we wanted we could have skipped naming the sequence and done
> cubelist:=[seq(i^3,i=-3..13)];
Some further examples of lists:
Make a list if the odd integers from 1 to 99
> oddlist:=[seq( 2*j+1,j=0..49)];
Make an expression sequence of all the natural numbers less than 137 which are 2 plus a multiple of 7 .
> twomodsevenseq:=seq(2+7*q,q=0..137/7);
Make a list of lists of numbers so that the nth element in your list is the list
,
starting with n=10 and ending with n=30.
> listoflists:=[seq([n,1/n^2],n=10..30)];
>
We can now generate sequences and lists with any command which takes in a single integer and produces an output. It even works with pictures. For instance suppose we want to lay bricks to make (a small part) of a sidewalk,a wall, or the like. First we modify our cube maker from Chapter 3 to make only red bricks that a eight units long, four units wide, and two units high. Lets call it "BL" (for bricklayer).
>
BL := proc (shift)
local v1, v2, v3, v4, v5, v6, v7, v8, front, back, left, right, bottom, top, box;
v1 := [0, 0, 0]+shift;
v2 := [8, 0, 0]+shift;
v3 := [8, 4, 0]+shift;
v4 := [0, 4, 0]+shift;
v5 := [0, 0, 2]+shift;
v6 := [8, 0, 2]+shift;
v7 := [8, 4, 2]+shift;
v8 := [0, 4, 2]+shift;
front := [v1, v2, v3, v4];
back := [v5, v6, v7, v8];
left := [v4, v8, v5, v1];
right := [v3, v2, v6, v7];
bottom := [v3, v4, v8, v7];
top := [v1, v2, v6, v5];
box := [front, back, left, right, top, bottom];
plots[polygonplot3d](box,color=red,style=patch, scaling=constrained );
end:
> BL([0,0,0]);
Suppose we want to make a stack of 10 bricks. Of course we could "personally" make each brick :
>
b1:=BL([0,0,0]):
b2:=BL([0,0,2]):
b3:=BL([0,0,4]):
b4:=BL([0,0,6]):
b5:=BL([0,0,8]):
b6:=BL([0,0,10]):
b7:=BL([0,0,12]):
b8:=BL([0,0,14]):
b9:=BL([0,0,16]):
b10:=BL([0,0,18]):
Then we produce a list to feed to "plots[display]". Note that we terminate with a semicolon. The reader might want to change it to a colon and execute the command to remind him/herself why - then change it back to a colon and execute again.
> tenbricks:=[b1,b2,b3,b4,b5,b6,b7,b8,b9,b10]:
Now we can look at the picture:
> plots[display](tenbricks);
We observe that what we are doing is in making the nth brick is just BL([0,0,2*n]); and this depends only on the integer "n" (we are starting with n=0). So to get our list all we need do is the following. Again we terminate with a colon ":" as we don't want to see the list in this form - we only want the picture.
> bricklist:=[seq(BL([0,0,2*n]),n=0..9)]:
> plots[display](bricklist);
We can shorten this with a compound command which simply combines the previous steps.
> plots[display]([seq(BL([0,0,2*n]),n=0..9)]);
However this is not necessarily good practice since brevity is gained at the expense of the ability to glance at the code and understand what it is doing.
> shiftedstack:=[seq(BL([n,0,2*n]),n=0..9)]:
>
> plots[display](shiftedstack);
Suppose we want only the even numbered bricks to shift
> evenshiftedstack:=[seq(BL([n*(1+(-1)^n)/2,0,2*n]),n=0..9)]:
>
> plots[display](evenshiftedstack);
An Introduction to
"for .. do ... od" and "if ... then ...fi "
Suppose we want to stack ten bricks but we want to alternate the colors. First we need to restore the color option to our "BL" command. Lets modify it to a "CBP" (chromatic brick placer) which allows us to specify the color.
> RWBstack:=plots[display]([CBP([0,0,0],blue),CBP([0,0,2],white),CBP([0,0,4],red)]):
> RWBstack;
>
Suppose we want to stack alternating blue and red bricks. The obvious way to do it "by hand" would be to manufacture the bricks one at a time assemble them into a list and use plots[display] to view the result. Indeed lets do a few steps in the process to see what we want to automate. This time we want to assemble the list as we go rather than waiting until the end. This should keep us from having to give a permanent name to each one. We simply give each new brick a temporary name and append it to the list. Lets let "CBS" stand for colored brick stack.
> CBS:=NULL;
> tmpbrick:=CBP([0,0,0],red):
> CBS:=CBS,tmpbrick:
> tmpbrick:=CBP([0,0,2],blue):
> CBS:=CBS,tmpbrick:
> tmpbrick:=CBP([0,0,4],red):
> CBS:=CBS,tmpbrick:
> tmpbrick:=CBP([0,0,6],blue):
> CBS:=CBS,tmpbrick:
> RBRBstack:=plots[display]([CBS]):
> RBRBstack;
>
Like the other processes we have been working with recently we can see that this is terribly repetetive. All we are doing is repeating the two commands "tmpbrick =CBP([0,0,x], somecolor); CBS:=CBS,tmpbrick;" and we are doing it ten times. What we need is the ability to tell the computer to do something a fixed number of times - in this case 10 times. The basic format for the command that does this is
:
> for n from 1 to 10 do n! od;
To have Maple print "n" followed by "n!" for n from 1 to 10 one could do any of the following:
> for n from 1 to 10 do [n,n!] od;
> for n from 1 to 10 do print(n,n!) od;
> for n from 1 to 10 do cat(`The factorial of `,n,` is ` ,n!,`.`) od;
>
To build an expression sequence called "squaresequence" containing the squares of the integers from 62 to 73 we could do the following:
>
>
SS:=NULL;
for w from 62 to 73 do SS:= SS,w^2 od;
If as is the case here we really don't care to see all the intermediate steps we can suppress the intermediate display by terminating with a colon ":". Change the semicolons to colons. After, type an "SS;" to see the final sequence.
> SS:=NULL:
> for w from 62 to 73 do SS:= SS,w^2 od:
So, for instance to build a sequence of ten red blocks like the one we did with "seq" we could simply
> bricks:=NULL: for t from 0 to 9 do bricks:=bricks,BL([0,0,2*t]) od:
>
brickstack:=plots[display]([bricks]):
brickstack;
Evidently we could use our "CBP" command as well but we need to add commands within the "loop" to tell Maple how to color the tiles. Suppose we want to start with a red brick on the bottom and then alternate with blue blocks from then. We need a way to issue the following instructions to the computer.
"if t is even then make the brick red otherwise make it blue".
We do this with the "if ... then ... else .. fi" command. Just as we end the "do" statement with "do" spelled backwards an "if..then .." statement is ended with "if" spelled backwards.
Like the "do..od " command the "if..fi" command is best explained with an example. Lets do the alternating red-blue bricks. There are lots of possible ways to do this. We will be very pedantic at first.
We need one additional tool. First recall that an integer is even if it yields a remainder of zero when divided by 2; it is odd if it leaves a remainder of 1 under the same circumstances. If we enter the command "a mod b;" Maple returns the remainder upon division of "a" by "b".
> 17 mod 2;
Now let "T" have some integral value and lets test whether its even or odd and make a red or blue brick accordingly.
> T:=17;
> if T mod 2 = 1 then CBP([0,0,0],red) else CBP([0,0,0],blue) fi;
> T:=18;
> if T mod 2 = 1 then CBP([0,0,0],red) else CBP([0,0,0],blue) fi;
Note that it is often convenient to enclose expressions of the form "a mod b" in parenthesis. This is optional but it tends to make Maple code easier to follow.
Note carefully the use of the "logical equal", "=" sign in the above as opposed to the "assignment equal", ":=".
Now all we have to do is modify the "body" of our "do..od" command which stacked ten red bricks.
>
bricks:=NULL:
for t from 0 to 9 do
if (t mod 2) = 1 then tmpbrick:=CBP([0,0,2*t],red) else tmpbrick:=CBP([0,0,2*t],blue) fi:
bricks:=bricks,tmpbrick; od:
> redbluebrickstack:=plots[display]([bricks]):
> redbluebrickstack;
>
It is no harder to use alternating red, white, and blue bricks. We just need to know how to have Maple choose among three alternatives. The format is "if ...then...elif.. then ...elif ...then...fi". Here is an example. Recall that the possible remainders upon division by 3 are 0,1, and 2.
>
bricks:=NULL:
for t from 0 to 9 do
if (t mod 3) = 2 then tmpbrick:=CBP([0,0,2*t],red) elif (t mod 3) = 1 then tmpbrick:=CBP([0,0,2*t],white) elif (t mod 3) = 0 then tmpbrick:=CBP([0,0,2*t],blue) fi:
bricks:=bricks,tmpbrick; od:
redwhitebluebrickstack:=plots[display]([bricks]):
> redwhitebluebrickstack;
>
We now have a very large measure of control over our piles of bricks. For instance we don't have to keep them all in the same orientation. Lets modify our code to build a different structure. We can use vertical bricks. Lets cook up a quick vertical brick placer called "VCBP".
>
VCBP := proc (shift,clr )
local v1, v2, v3, v4, v5, v6, v7, v8, front, back, left, right, bottom, top, box;
v1 := [0, 0, 0]+shift;
v2 := [0, 0, 8]+shift;
v3 := [0, 4, 8]+shift;
v4 := [0, 4, 0]+shift;
v5 := [2, 0, 0]+shift;
v6 := [2, 0, 8]+shift;
v7 := [2, 4, 8]+shift;
v8 := [2, 4, 0]+shift;
front := [v1, v2, v3, v4];
back := [v5, v6, v7, v8];
left := [v4, v8, v5, v1];
right := [v3, v2, v6, v7];
bottom := [v3, v4, v8, v7];
top := [v1, v2, v6, v5];
box := [front, back, left, right, top, bottom];
plots[polygonplot3d](box,color=clr,style=patch, scaling=constrained )
end:
> VCBP([0,0,0],red);
>
bricks:=NULL:
END:=0:
for t from 0 to 9 do
if (t mod 3) = 2 then tmpbrick:=VCBP([END,0,0],red); END:=END+2; elif (t mod 3) = 1 then tmpbrick:=VCBP([END ,0,0],white); END:=END +2; elif (t mod 3) = 0 then tmpbrick:=CBP([END,0,0],blue); END:=END+8; fi:
bricks:=bricks,tmpbrick; od:
coloredbricks:=plots[display]([bricks]):
coloredbricks;
Removing the constaints does a better job of packing the image into the viewing window at the price of departure from uniform scale.
> coloredbricks:=plots[display]([bricks],scaling=unconstrained):
> coloredbricks;
>