My Blocks
A My Block is a block you make yourself. You take a group of blocks that do one job, give the group a name, and from then on that name is the block.
Blocks reference
| Block | What it does |
|---|---|
define grab | The head of a definition. Whatever is attached under it is what the block does. There is exactly one of these per My Block. |
grab :: myblocks | Runs the definition. Use it as many times as you like. |
define turn (degrees) | A definition with an input. degrees is a name for whatever number the caller types. |
turn (90) :: myblocks | Runs it with 90 in place of that name. |
Why bother
Copying blocks works perfectly — right up until the copied thing has to change. Here are two robots driving a square, one with a My Block and one with the blocks copied four times, and then somebody asks for a bigger square.
a My Block, used four times
the same thing copied
↑ the fourth copy still says 2
The bug is not in any block. It is in the fact that the same idea was written down four times, and only three of them were updated.
The right-hand robot is not running a broken program. It is running four programs that were supposed to be identical, and three of them were updated. Nothing is misspelled, nothing reports an error, and the only evidence is a square that does not close.
The left-hand program is these two listings, and nothing else:
define side move [forward v] for (3) [rotations v] :: movement move [right: 100 v] for (0.5) [rotations v] :: movement
when program starts :: events hat repeat (4) side :: myblocks end
- One place to fix. A turn defined once and used eleven times is corrected in one place. Copied eleven times, it is corrected in eleven — and one always gets missed.
- Readable. A program that reads turn-right, drive-to-wall, grab explains itself. The same program written out in full does not.
Inputs make it general
A My Block can take a value. Turn-right is useful; turn (degrees) is far more useful, because one definition then serves every turn in the program. The value is given a name when the block is made, and that name is used inside the definition wherever the number belongs.
The name is the part that trips people up. Inside the definition, (cm) is not a variable you have to set anywhere — it is a hole, and it is filled in with whatever the caller typed, every time the block is called.
Example 1 — a block that thinks in centimetres
EV3 movement blocks count wheel turns, and nobody measures a table in wheel turns. One turn of a standard driving wheel carries the robot about 17.5 cm, so driving 50 cm means asking for 50 ÷ 17.5 rotations. Doing that sum in your head, at every movement block, all program long, is how mistakes get in.
Put the sum inside a My Block once and the whole program can be written in centimetres. Change the two numbers below and watch where they end up.
These two numbers are the whole interface. Everything below is what the definition does with them.
what you write in the program
drive (50) cm at (75) % :: myblocks
the definition — (cm) and (speed) are names, not values
define drive (cm) cm at (speed) % set movement speed to (speed) % :: movement move [forward v] for ((cm) / (17.5)) [rotations v] :: movement
the same blocks, with your two numbers put in
set movement speed to (75) % :: movement move [forward v] for ((50) / (17.5)) [rotations v] :: movement
The green division block is doing the only real work: the movement block underneath counts wheel turns, and you asked in centimetres. One turn of these wheels carries the robot 17.5 cm, so 50 cm is 50 ÷ 17.5 = 2.86 turns.
Set the distance to 17.5, 35 or 70 and the part turn disappears — those are whole numbers of wheel turns. Every other distance leaves a fraction, which is exactly why the division belongs inside the My Block and not in your head.
Two inputs, and the definition is three blocks. Note what has actually been gained: not fewer blocks — the same blocks run either way — but a program you can check against a ruler. drive (50) cm at (75) % is either right or wrong about the table, and you can tell which without running it.
Typing a fixed number inside the definition instead of using the input is the classic slip — the block then ignores whatever it is given and always drives the same distance:
define drive (cm) cm at (speed) % set movement speed to (speed) % :: movement move [forward v] for ((50) / (17.5)) [rotations v] :: movement
That block takes a distance and throws it away. It will pass every test you run at 50 cm.
Build a script and run it
Here are those two My Blocks and a 100 × 100 cm mat. Drag blocks into the script, set the numbers, and press Run — the base always starts in the left-hand corner facing up the mat. Say where you think it will finish before you press it; the ruler is there to settle the argument.
Two My Blocks, a 100 × 100 cm mat and a ruler. Drag blocks into the script — or press one to add it — set the numbers, then Run and see whether the base ends up where you said it would.
my blocks — drag or press
the script — 0 / 8 blocks
Empty. Drag a block in from above, or press one.
Nothing to run yet. A square is four drives and four turns — try predicting the distance before you press Run.
Eight blocks is enough for a square with 40 cm sides, and not enough for anything you cannot picture. If a shape needs more than that, it wants a repeat (4) around it — which is the next thing a My Block usually grows into.
Example 2 — a block that thinks in degrees
The Gyro Sensor gives an accurate turn, and it costs five blocks every time: reset the angle, start turning, wait until the reading is a few degrees short of the target, stop. Those five blocks are about the sensor. What the program wants to say is turn 90.
define turn (degrees) [2 v] reset angle :: sensors start moving [right: 30 v] :: movement wait until <([2 v] angle :: sensors) > ((degrees) - (4))> stop moving :: movement
The − 4 is the overshoot allowance from the Gyro Sensor module: the robot carries on turning for a moment after the motors are told to stop, so the block asks for less than it wants and lets it coast in. Written here, it is decided once. Written out at every corner, it is eleven chances to forget it.
Now the input earns its keep — because the shape a robot draws is decided entirely by that one number. A closed shape turns through 360° in total, so a shape with n corners turns 360 ÷ n at each one.
the whole program
when program starts :: events hat repeat (4) move [forward v] for (2) [rotations v] :: movement turn (90) :: myblocks end
what turn is, once
define turn (degrees) [2 v] reset angle :: sensors start moving [right: 30] :: movement wait until <([2 v] angle :: sensors) > ((degrees) - (4))> stop moving :: movement
Nothing in the program mentions the gyro. It is all inside turn, which is the point: the sensor is an implementation detail of turning, and the program should be about the shape.
Same definition, same drive block, same length of side. The only edit between a triangle and a pentagon is turn (120) :: myblocks becoming turn (72) :: myblocks. That is what a well-chosen input does: it turns a program that draws one shape into a program that draws shapes.
The same block also turns left, if you let it. Give the definition an if … then … else and a negative input means anticlockwise:
define turn (degrees) [2 v] reset angle :: sensors if <(degrees) > (0)> then start moving [right: 30 v] :: movement wait until <([2 v] angle :: sensors) > ((degrees) - (4))> else start moving [left: 30 v] :: movement wait until <([2 v] angle :: sensors) < ((degrees) + (4))> end stop moving :: movement
One block, two directions, and every program that uses it gets the left turn for free the moment this definition is edited.
When to make one
The signal is repetition: the same sequence appearing in more than one place. That is the moment to name it. Making a My Block for something used once adds a layer without adding clarity.
The second signal is a unit. If the blocks convert between what you think in (centimetres, degrees, seconds) and what the hardware counts (rotations, sensor readings), that conversion is worth a name — it is the thing you will otherwise get wrong.
More data tutorials
- Variables — Give the robot a number it can remember and change as it works.
- Lists — Store several readings in order, so the robot remembers them all.
- Broadcasting a message — Let one stack of blocks tell another stack to start.
- Random numbers — Make the robot behave unpredictably on purpose.