Order of Operations
When looking at an expression like x + 10 * y
, we know that the first thing to do is 10 * y
and then add x
, but it isn’t naturally obvious. Let’s look at why with a tree for x + 10 * y
:
graph TD; subgraph orderofop[x + 10 * y]; add(+) --> x(x); add --> mul("×"); mul --> 10(10); mul --> y(y); end
With the tree above, we can tell that we do the multiplication first, and the addition second. But if we use the symbolic notation without the tree, the most natural thing for people who are used to left-to-right reading is to solve it like (x + 10) * y
. The order in which we have to solve it becomes clear when using the tree:
graph LR; subgraph p1["(x + 10) * y"]; mul("×") --> add(+); mul --> y(y); add --> x(x); add --> z10(10); end subgraph p2["x + (10 * y)"]; add2(+) --> x2(x); add2 --> mul2(*); mul2 --> 10_2(10); mul2 --> y2(y); end p1 -.-> p2;
Properties
The properties of operations (commutation, association, distribution) are also easy to visualize with the tree:
Commutation
graph LR; subgraph ab[a + b]; add(+) --> a(a); add --> b(b); end subgraph ba[b + a]; add2(+) --> b2(b) add2 --> a2(a) end ab -.-> ba;
Association
graph LR; subgraph a_bc["a + (b + c)"]; add(+) --> a(a); add(+) --> add2(+); add2 --> b(b); add2 --> c(c); end subgraph ab_c["(a + b) + c"]; add3(+) --> add4(+); add3 --> c2(c); add4 --> a2(a); add4 --> b2(b); end a_bc -.-> ab_c;
Distribution
graph LR; subgraph 2_xy["2 * (x + y)"]; mul("×") --> 2(2); mul --> add(+); add --> x(x); add --> y(y); end subgraph 2x_2y["(2 * x) + (2 * y)"]; add2(+) --> mul2(*); add2 --> mul3(*); mul2 --> 2_2(2); mul2 --> x2(x); mul3 --> 2_3(2); mul3 --> y2(y); end 2_xy -.-> 2x_2y
(note: distribution hasn’t been implemented in my code yet)
Levels/Branches
The one downside to the tree format is that there are always different levels. For example, when adding 1 + a + 3
, it doesn’t matter which 2 numbers you add first, but with the tree you have to have an order. So, the tree will look like this:
graph TD; subgraph 1a3["(1 + a) + 3"]; add(+) --> 3(3); add --> add2(+); add2 --> 1(1); add2 --> a(a); end
The ideal solution we want is a + 4
, but in this format, it will first add 1 + a
, which isn’t possible since a
is a variable, and then add 3
to 1 + a
. This will just result in 1 + a + 3
again.
What we want instead is something like this:
graph TD; subgraph 1a3["1 + a + 3"]; add(+) --> 1(1); add --> a(a); add --> 3(3); end
Now, all of them are on the same “level”. Since addition is commutative, we can just re-arrange the expression and add 1
and 3
like so:
graph LR; subgraph 1a3["1 + a + 3"]; add(+) --> 1(1); add --> a(a); add --> 3(3); end subgraph a13["a + 1 + 3"]; add2(+) --> a2(a); add2 --> 1_2(1); add2 --> 3_2(3); end subgraph a4["a + 4"]; add3(+) --> a3(a); add3 --> b4(4); end 1a3 -.-> a13 -.-> a4;
And we’ve got a + 4
. Perfect!
So, the tree and the textual format are both good for different scenarios, but the tree is helpful when visualizing an expression, and the textual form is good for daily usage.
Go To: