By the end of this lesson, you should be able to...
So we first learned about the various basic kinds of data we can store inside variables for storybrew. While we know a few things like basic arithmetic operations, knowing how you can manipulate variables is incredibly important. Operators help perform actions its receiving variables, known as operands. You’re already familiar with these in any math class; they don’t call it the order of operations for nothing. In an expression like
16+9, the operator is the
+ symbol, with the operands being
Certain operators ask for different amounts of operands. Operators that take only one operand are known as unary operators. Two operands, such as our addition expression above, are known as binary operators. Only one operator exists that takes three operands, known as the ternary operator. We’ll talk about those first, cover some specialized operators, and then some of the logic behind them.
Unary operators only ask for one operand. This table covers some of the more practical unary operators.
||Pre-increment by 1|
||Pre-decrement by 1|
||Post-increment by 1|
||Post-decrement by 1|
And some example usage:
1 2 3 4
int startingNumber = 11; int negativeNumber = -startingNumber; // -11 startingNumber++; // startingNumber + 1 -> 12 startingNumber--; // startingNumber - 1 -> 10 again
You may be wondering what the differences are between the pre and post versions of incrementing and decrementing. Could they be similar to the communitive property of algebra, where it doesn’t make a difference which side it is? While you may be inclined to think that, there is a slight difference, which is when the incrementing/decrementing occurs. Take a look at this example:
1 2 3
int Aoba = 4; // We start off with Aoba at a tender value of 4. (She's actually over 18, thank you.) int Hifumi = ++Aoba; // Increment Aoba to 5 first, then assign that to Hifumi. Hifumi = 5, Aoba = 5 int Yun = Aoba++; // Assign Aoba to Yun first, then increment Aoba by 1. Yun = 5, Aoba = 6
As the example demonstrates, the primary difference is at what point is the righthand value upon evaluation. Writing the prefixed manner would apply the increment/decrement first, then assign, while the postfixed manner lets the value get assigned first, then get increment or decrement.
The following code-block break down prefixed and postfixed differences even further by providing their longer equivalents.
1 2 3 4 5 6 7
int Aoba = 4; Aoba = Aoba + 1; int Hifumi = Aoba; // Increment Aoba to 5 first, then assign that to Hifumi. Hifumi = 5, Aoba = 5 int Yun = Aoba; Aoba = Aoba + 1; // Assign Aoba to Yun first, then increment Aoba by 1. Yun = 5, Aoba = 6
Post-increment and post-decrement are what’s known as primary operators. They have the highest precedence when compared to other unary operators. This isn’t really a big deal, but know that these two operators are a bit more special than the other unary operators.
There is one other unary operator that hasn’t been plugged in yet, which is logical negation, or
!x. This will discussed in a later chapter when we utilize more logical operators in our code.
Binary operators ask for two operands. These are the most common operators you’ll encounter, as basically all the arithmetic, mathematical junk fall under this tree. Because of this, this section is pretty much titled as the arithmetic operators, as they’ll be the most important binary operators to encounter.
Remember that integer division completely ignores the decimals when dividing. That is to say,
1 and not
1.5. If you need the decimal, at least one of the numbers needs to have floating-point precision. Simply adding
.0 to one of the values, like
3.0/2 will suffice.
The only operator of special note here is the modulus operator, as often the newbie programmer gets a bit tripped up with its functionality. Simply remember that it’s just like integer division, only that it gives the remainder instead of the result. As such:
1 2 3 4 5
int pizzaSlices = 8; int extraSlices; extraSlices = 8 % 3; // remainder 2 extraSlices = 8 % 2; // remainder 0 extraSlices = 8 % 7; // remainder 1
Some special phenomena are possible when utilizing modular arithmetic (as what this is called), but that’s a can of worms for another day.
The ternary operator accepts three operands and is often an underrated but incredibly useful operator to make. It’s easier to explain by looking at its syntax, then demonstrating it in an example.
<condition-to-evaluate> ? <result-if-true> : <result-if-false>;
The contents within
<condition-to-evaluate> can be any sort of logical evaluation that results in a
false. This is known as the
bool type that we talked about in the previous chapter. It’s possible to get these
false values with mathematical expressions, as shown:
1 2 3 4 5 6 7 8
// If the particle is on the bottom-half of the screen, put it towards the right side of the screen. // Otherwise, put it on the left side. Vector2 particleLocation = new Vector2(0,180); particleLocation.X = (particleLocation.Y > 240) ? 600 : 40; // X will be 40. // If I'm hungry, I'll get a hamburger. Otherwise, get a salad. bool amIHungry = true; string myLunch = (amIHungry) ? "Hamburger" : "Salad"; // BURGER TIME!
These expressions use relational operators, which are virtually identical to the concept of inequality in our math classes.
Observe that the contents in
<result-if-false> may be both possibilities for the variable to assign, meaning that they should share the same type as that variable.
The ternary operator will be discussed more heavily in the next section, so don’t sweat it if you feel it’s a bit confusing. For now, we’re just introducing the different kinds of operators we have available, so treat it as a sampler. It’ll grow on you, like that bratty cute mascot in any shonen anime.
All the arithmetic operators have a shorthand with the assignment operator
= that allows for more convenient evaluation. It basically fuses both assignment and the respective arithmetic operator.
The lefthand variable’s current value will be one operand with the arithmetic operator, and the righthand value is the other. Whatever is the result will get assigned into the lefthand variable. As such, the following segments of code are equivalent:
float myWallet = 0.50f; myWallet += 10.43f; // I have $10.93 in my wallet. :(
float myWallet = 0.50f; myWallet = myWallet + 10.43f; // I still have $10.93 in my wallet. :(
As mentioned earlier, it’s possible to compare different values together and reach a conclusion of
false. These sorts of evaluations use relational operators, binary operators that make a comparison and return a
||Equality||Checks if the two operands are equal. If they are, return
||Inequality||Checks if the two operands are not equal. If they are not, return
||Greater Than||Checks if the left operand is greater than the right operand. If so, return
||Less Than||Checks if the left operand is less than the right operand. If so, return
||Greaten Than Equal||Checks if the left operand is greater than or equal to the right operand. If so, return
||Less Than Equal||Checks if the left operand is less than or equal to the right operand. If so, return
You know the drill. Example time!
1 2 3 4 5
bool kreygasm; kreygasm = (14 > 8); // true kreygasm = (19.4f == 19f); // false kreygasm = ("Hello world!" != "WutFace"); // true kreygasm = (67 <= 67); // true
We’ll harness the power of these relational operators when we let our programs make different decisions depending on whether something is true or false. Before that though, we just need to discuss a little with how the program evaluates these operators.
Just like in math class, certain operators have a higher priority than others when it comes to evaluation. The popular concept of PEMDAS taught in math classes generally applies the same way here with programming. For instance, the
/ operators will precede any
- operators, and so forth.
Remember that you can elevate the precedence of an operator by wrapping the expression up in parentheses. The contents in the parentheses will be evaluated first, just like PEMDAS.
When operators have the same precedence, they are then evaluated based on associativity. Most general operators, such as the arithmetic and relational operators, are left-associative, which means they are evaluated in order from left to right. This means that an expression like
x * y / z is read as
(x * y) / z. The scant few operators that don’t do this are right-associative, which means that they are evaluated in order from right to left.
The only right-associative operators to consider are all the assignment operators and the unary operators. This just means that when you have an expression such as
a = b = c,
c will get assigned to
b will get assigned to
a. Consider another example that uses some more non-trivial assignment operators, and its equivalent piece of code. What are the results of
1 2 3
int yazawaNico = 25252, makiChan = 100; int loveNest = 69; loveNest += yazawaNico -= makiChan;
Think of the right-associativity! Start from the right, and don’t forget to slowly overwrite the values as you go. Take it one step at a time.
1 2 3 4
int yazawaNico = 25252, makiChan = 100; int loveNest = 69; yazawaNico -= makiChan; loveNest += yazawaNico;
The final values should be
yazawaNico: 25152, and
If you find this confusing, just think of it like this:
When in doubt though, you can always be liberal with parentheses. Just be sure to realize that there is a slight trade-off in readability.