Lövely Code #01 - Ternary Operations

Hello world, err, lövers! My name is Dale James and I'm writing to introduce you to one of my favourite things you can do in Lua, which is called a Ternary Operation.

I grew up playing video games - my first gaming machine was a Commodore 64 (which I still have, somewhere, with a neat collection of tapes). The more games I played, the more I dreamed of being able to produce my own games. Still to this day whenever I play a game I consider how it was built, what works well and how I could improve it. I have no formal education in programming of any sort, except maybe those 2 lectures I attended for ActionScript, but I'm not sure they count as I remember correcting the lecturer several times. I spent countless hours after school messing with lots of (what I now consider to be) awful frameworks and engines, so you could imagine the rush I got when I first discovered the pure awesomeness that is Löve. No other 2D framework comes close to it in terms of elegance, ease of use, and documentation (oh, that documentation!).

This series of articles, dubbed Lövely Code, is intended to focus on the elegance that blossoms from Löve and Lua. Writing elegant code is very rewarding, fun, and it just looks so darn cool! So, on to Lövely Code #01:

This article assumes that you have a basic knowledge of Lua concepts, particularly data types and expressions.

Ternary?

Many readers may not be aware of the concept of a ternary operator or operation. In Lua we have access to several types of operation - unary, binary and ternary.

A unary operation is an expression with a single operand, or a single 'input value'. According to the Lua 5.1 Manual, Lua features three types of unary operators:

a = -1        --negation
b = not x     --logical negation
c = #"purple" --length, works with tables & strings

A binary operation is an expression with two operands. I bet you can see where this is going. There are lots of binary operators so I'll just list a few of them:

a = 1000 + 337 --need I explain this?
b = a or 1337  --if a is nil or false then b becomes 1337
c = b > 9000   --c becomes true if b is over 9000
d = a          --also binary!
e = "some" .. "thing" --concatenation is actually a binary operator

Now here's the fun part.

As you may have guessed, a ternary operation is one that has three operands. "Three?!" you say? "How does that work? ", you ask, or "Prey tell how this most elusive of operations achieves its resolution, old chap", if you're English like myself, of course I wrote this article whilst wearing my best top hat and monocle, curling my mustache, and eating a lime.

It's actually rather simple. A ternary operation is often used as a substitute for an if/then/else statement.

Delve Into the Secrets

In C-like languages there does exist an operator for ternary expressions, but Lua being Lua, we are treated to a much more elegant and human-readable method for creating these most wondrous of wonders; a combination of and / or operators makes for a beautifully simple ternary operation.

--a simple if/then/else...
if a then
    x = b
else
    x = c
end

--is equivalent to:

x = a and b or c --three operands!

Whoah! What's going on there?! It's actually pretty simple if you break the expression down and evaluate it in the same manner as the Lua VM. If we check the manual for order of precedence we find that and is always evaluated before or. We also know that an and statement outputs the rightmost operand if the left is true, and that or outputs the left unless is it false. Sound confusing? Let's look at this in a way we can all understand.

To The Terminalmobile!

I recommend opening up a Lua console and following along. Start a new Lua session and try each line in sequence.

x = a and b or c
print(x)

Our original statement. This would actually always evaluate to nil since we have not specified a, b or c, so let's give ourselves some sample values to work with.

a = 11
b = "variable b"
c = "variable c"

Now, we're going to use the same expression as above. Before you run the command, take a moment to consider what the output will be...

Ready? Then let's try it!

x = a and b or c
print(x)

You should see the output variable b. What happened is that we said "if a is true then x is equal to b, otherwise it is equal to c". We did a = 11, which in Lua means that if a then will evaluate to true. Just like a basic if/then/else!

So what happens if a is not true? Go ahead, try it:

x = not a and b or c
print(x)

This time you should see the output variable c. Similarly to before, this is because we said "if not a is true then x is equal to b, otherwise it is equal to c". This time, not a evaluates to false. Allow me to demonstrate what is going on here (there is no need to input this block into your terminal):

x = not a and b or c                         --equivalent to
x = false and "variable b" or "variable c"   --equivalent to
x = (false and "variable b") or "variable c" --equivalent to
x = false or "variable c"                    --equivalent to
x = "variable c"                             --tada!

Got it now? This is how a ternary expression works. As you can see, it's actually very simple once you understand the nature of and/or.

Let's try another example:

x = a < 10 and b or c

Oh my science. Just what is going on there?! Now we have a binary expression embedded in our ternary expression........! Don't be fooled: this is simple stuff. "How can you say that?!" you yell at me. Well, if we consider that our ternary expression is rather like an if/then/else we see that it is indeed simple stuff (no need to execute this block either):

if a < 10 then
    x = b
else
    x = c
end

That wasn't so painful was it? So now if we go back and output our new x, what would we see?

print(x)

You should see variable c again. This is because our value a is greater than 10: a < 10 = false. Go ahead and change the binary expression to use greater-than:

x = a > 10 and b or c

We are all over this now! We know what's going happen here! Our x is going to equal variable b, just like you expected.

The Cave(at)

One important thing to note when writing ternary operations is that the 'middle' operand cannot be false/nil (b in our example) ! This is because of the nature of and/or. Try it and see what happens.

x = nil and nil or true
x = nil or true
x = true
--or
x = true and nil or true
x = nil or true
x = true

However, if you need this type of operation, you can simply 'invert' the operands:

x = a == nil and b or a --solved.
x = a == nil and b == false or a --don't do this!

A Winner Is You

Congratulations, you just learned how to write ternary expressions!

Throughout this article I have repeatedly stated that this is a simple concept. In reality, it can be hard to understand at first, but once you get it, it becomes a beautifully simple, elegant way to form expressions in your code.

So where exactly should you be using these most excellent operations?

There is no universal answer. Fear not though, for there are guidelines! Try to avoid convoluted ternary statements, after all we write them for our own sake; use them to make your programs more readable, more elegant, more lövely. An interesting example could be for setting a variable based on whether another exists or not (or setting a default), the condition is also the 'middle' operand:

--if statement basis
if a then
    x = a
else
    x = b
end

--could also be
x = a and a or b --this can be further simplified to (and avoids the caveat!)
x = a or b --this binary operation tastes great with functions

Code to die for.

For further reading, as always I recommend lua-users.org which is where I learned how to write these, and many other Lua concepts. You may find there that more complex forms of the ternary operation can be written, but they truly are complex and are (waaaaay) beyond the scope of this document.

I hope you have enjoyed this article, the first in the Lövely Code series.

Comments

Germanunkol's picture

Great post!

I don't want to be picky, but I disagree with the statement:

"if a is true then x is equal to b, otherwise it is equal to c"

Instead, I believe it should be:

"if a is true AND b is true then x is equal to b, otherwise it is equal to c"

Germanunkol's picture

P.S.

I've used quite a few things from this tutorial already - thanks!

Not necessarily just the ternary operations (although those, too). I especially like the initialisation thing:

[code]
function myFunc( x_coord )
        x_coord = type(x_coord) == "number" and x_coord or 0
end
[/code]

Lafolie's picture

Thank you. Glad to see you enjoying the flexibility of Lua.

Your previous comment is somewhat correct. Let's say we do `x = a or false` - this is a redundant expression. If we are given that `a == false` the expression evaluates something like `x = false or false`. If we truncate the expression we are left with `x = a`. Given that `a == false` we are now performing the more sanity-friendly `x = false`.

Lua is a dynamically typed language which allows for subtleties such as this. I guess the simple unary assignment operation could be expressed as  `f = x or false`.

We can extrapolate this to `x = a and b or c` as I did in the article. I did also mention that these operations don't work as intended if the `b` value is `nil`/`false` due to the uhm, laws, I touched on just now.