Don’t repeat yourself (let the computer do it)

Instructions can call instructions can call instructions…

Jack Holland
Understanding computer science
6 min readDec 7, 2013

--

The first version of Cake consists of a few arithmetic instructions (+, -, *, /) and parentheses, allowing for flexible, if verbose representations of basic arithmetic. This is a good start, but let’s clear up a few ambiguities. For instance, what if we want to subtract three numbers at a time, like 5 minus 2 minus 1? Intuitively, the Cake code might look like this: 5 - 2 - 1. That makes sense to us, but as it stands, Cake will get confused. Why? Take another look at the definition of -:

  • x - y: subtracts y from x

If Cake sees 5 - 2 - 1, which pieces make up x and which pieces make up y? Said another way, which subtraction comes first? One interpretation is that x is 5 and y is 2 - 1, but another is that x is 5 - 2 and y is 1. It’s not clear how Cake will interpret these instructions. One solution is to add parentheses, like we did last time: 5 - (2 - 1) removes the ambiguity, since first Cake computes 2 - 1, returning 1, and then computes 5 - 1, returning 4. Alternatively, we could add parentheses the other way: (5 - 2) - 1, so that Cake computes 5 - 2, returning 3, and then 3 - 1, returning 2. As you can see, the answer changes depending on where the parentheses are placed.

Does this help you remember PEMDAS? I’ve never liked mnemonics but I do like this picture.

Since Cake is supposed to be intuitive, let’s resolve what it should do when it sees 5 - 2 - 1 without parentheses the way we usually do: PEMDAS. You may or may not remember this mnemonic from middle school math. It stands for parentheses, exponents, multiplication, division, addition, subtraction. That’s a mouthful, so let’s break it down (even if you remember how it works, it may help to relearn the details). To start, parentheses get evaluated before everything else. In other words, it doesn’t matter what’s around a pair of parentheses, everything inside them gets evaluated before combining with the stuff outside them. Example: in the case of (1 + 2) * 3, first 1 + 2 gets dealt with, then 3 is incorporated.

Next up are exponents. Cake doesn’t yet have an exponent instruction so let’s add one:

  • x ^ y: raise x to the power of y

For example, 2 ^ 3, pronounced “two to the third” or “two to the three”, raises 2 to the power of 3. This is the same as 2 * 2 * 2. Similarly, 2 ^ 4 is the same as 2 * 2 * 2 * 2. Any number to the first is that same number: 2 ^ 1 is 2 and 3 ^ 1 is 3. Any number to the power of 0 is 1. Therefore, 4 ^ 0 and 10 ^ 0 both return 1.

So PEMDAS says that the exponent instruction, ^, gets a higher priority than the other instructions, +, -, *, and /. Multiplication and division have the same priority. If more than one * or / appears in a row, the left one gets evaluated first. So, in the case of 4 * 3 / 2, first 4 * 3 is computed, returning 12, then 12 / 3 is computed, returning 4. Addition and subtraction have the lowest priority. Everything else gets handled before they do. As with multiplication and division, addition and subtraction are handled left-to-right if there is any uncertainty.

Instructions higher up take priority over instructions lower down

Let’s put this together with a more comprehensive example: 5 ^ 2 + 7 * 3. Start at the left and follow PEMDAS; exponents come before everything but parentheses, so 5 ^ 2 gets evaluated right away, returning 25 (since 5 * 5 is 25). Next comes an addition sign, +. But addition has a lower priority than the multiplication sign, *, to the right. Thus, even though + is to the left of *, * has a higher priority and goes first. That means 7 * 3 gets computed, returning 21. At this point, the result looks like 25 + 21, which simply returns 46. If you understand this example, then you’re ready to move on. If PEMDAS is still a bit unclear, work through the examples again until it’s clear, since I’ll assume you know it from now on. Cake will use it whenever it computes math.

Adding PEMDAS has fixed one flaw in Cake, but there are still more problems. Have you heard of the half-your-age-plus-seven rule? Divide your age by 2 and then add 7 to that — this is the lowest age of a person you can date before it becomes socially unacceptable. Try it out with your own age and you’ll see that it’s actually a pretty decent guideline. Let’s say you and your friends have just heard about this rule and want to figure out the youngest date-able age for each of you. Your first friend is 24, so you compute with Cake: 24 / 2 + 7, which because of PEMDAS works out as it should: 19. Your next friend is 29, so compute 29 / 2 + 7, which returns 21.5 (the rule doesn’t specify if and how one should round the age if it’s not a whole number). Another friend is 26, so compute 26 / 2 + 7, which returns 20.

This method always produces the correct result (in terms of following the rule), but it’s getting pretty tedious. What if you have five more friends who want their numbers computed as well? You can keep repeating the calculations, but it seems unnecessarily redundant. If you get tired of repeating them, you might get sloppy and make errors. What if you could make a shortcut that represents those calculations? Instead of manually typing out the instructions each time, there would be one instruction that represents the whole process. Here’s what it might look like:

  • youngest x: x / 2 + 7

Using this new instruction, youngest 24 returns 19, youngest 29 returns 21.5, and youngest 26 returns 20. Using this new instruction, you no longer have to mindlessly repeat rote calculations over and over. By defining instructions that use other instructions, you can create programs of enormous complexity that are still understandable since the complicated calculations are hidden behind intuitive names. Unfortunately, the half-your-age-plus-seven rule doesn’t have a more concise name, and writing out half-your-age-plus-seven would hardly save time compared to what it calculates, so youngest will have to do. If this sounds unconvincing, remember that most rules have names more appropriate for instructions.

Naming issues aside, adding instructions like youngest adds a minor problem. How should Cake compute youngest 20 + 3? PEMDAS doesn’t say what to do in this case. We could make a rule that decides what should come first (e.g. first do 20 + 3, then do youngest on the result), but if you’re like me, you might find something like that hard to memorize and intuitively understand. Instead, let’s slightly modify the youngest instruction:

  • youngest(x): x / 2 + 7

Everything youngest needs should be put inside parentheses, like this: youngest(24), which like before returns 19. To resolve the ambiguity above, we can write youngest(20 + 3), which is the same as youngest(23), which returns 18.5.

You might have noticed that the parentheses needed for youngest, while helpful, make Cake a little less like English and a little more like the alien, arcane symbolism of a programming language. This is OK, actually. While programming languages should be easy to read, they don’t necessarily have to look like English or any other informal language. We’ll talk about this a lot more later, but for now recognize that while starting with an English-looking language is a great learning tool, eventually you’ll need to become comfortable with structural characters like these: ( ) [ ] { } < > : ; & |

That’s in the future, though. For now, focus on the meaning as well as the grammar. How can you use instructions that call other instructions to your advantage? Can you see the possibilities of such a tool?

Image credit: Aunt Sally, PEMDAS

--

--