When it comes to organizing your logic in a program, one tool that can do a lot of heavy lifting for you is short-circuit evaluation. In this article, I’ll give a little bit of background around short-circuit evaluation, describe what short-circuit evaluation is, and why I think you should use it. Let’s get into it!
Table of Contents
Background
In most programming languages, we have constructs that enable us to compute boolean expressions. A boolean expression is any expression which evaluates to true or false—even if the subexpressions contain values that aren’t booleans (i.e., numbers, strings, functions, etc.). Here are a few examples from my favorite programming language, Python:
5 < 6 # Evaluates to True True and False # Evaluates to False not True or 17 > 4 # Evaluates to True bool([]) # Evaluates to False 5 > 6 > 7 # Evaluates to False
As you can see, boolean expressions can come in many forms from arithmetic comparisons to function calls. Naturally, it’s probably no surprise that boolean expressions are extremely useful. In fact, we often use them when we wish to branch in our code (e.g., loops, if statements, etc.). As a result, programming languages often have a lot of tricks to make these expressions as powerful and expressive as possible. One of those tricks is the topic of today’s article: short-circuit evaluation.
What Is Short-Circuit Evaluation?
In a previous article, I had written about flags and why I recommend folks to avoid them. Interestingly, one of the best arguments against flags is missing out on the beauty of short-circuit evaluation. Let’s look at an example:
positive_number = -1 flag = true while flag: user_input = input("Give me a positive number: ") if user_input.lstrip("-+").isdigit(): positive_number = int(user_input) if positive_number > 0: flag = true
In this example, we’re trying to get the user to provide us with a positive number. If we get anything other than a positive number, we prompt the user again. To make sure we have a positive number, we first have to make sure we have a number. Only then can we convert the string to an integer to check if it’s positive.
As an educator, I see this type of code a lot with beginners. It seems they understand branching with if statements really well, but they have trouble transitioning that same idea to loops. Funnily enough, there’s a really clean solution that involves shifting these two conditions to the loop condition:
user_input = input("Give me a positive number: ") positive_number = -1 while not user_input.lstrip("-+").isdigit() and int(user_input) <= 0: user_input = input("Try again: ") positive_number = int(user_input)
And of course, there are a dozen of other ways to accomplish the solution. That said, I chose this one to demonstrate the concept for the day: short-circuit evaluation.
Previously, we had a series of nested if statements. The idea behind these if statements is that we care a lot about the order of their execution. If the first one fails, we don’t want to even attempt the second one. Otherwise, our program would crash:
>>> int("dfas") Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> int("dfas") ValueError: invalid literal for int() with base 10: 'dfas'
Of course, nesting if statements is a pain, and every time we add a level of indentation we introduce complexity. As a result, I prefer to lean on short-circuit evaluation. The idea is that we can accomplish the same exact thing as nesting through careful ordering of our boolean expressions. For example, we could combine our previous if statements as follows:
positive_number = -1 flag = true while flag: user_input = input("Give me a positive number: ") if user_input.lstrip("-+").isdigit() and int(user_input) > 0: positive_number = int(user_input) flag = true
Now, instead of nested if statements, we have one if statement where the two conditions we care about are bound by a single boolean operator (and). With AND specifically, if we know the first value evaluates to false, we can stop evaluating; the entire expression is false. Here’s the truth table if you don’t believe me:
X | Y | Output |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
As you can see, any time the first value is false, we can assume the output will be false. We know this because AND will only return true when both values are true. Knowing this fact, we can cheat a bit when we evaluate expressions. As soon as the first value comes out false, we stop evaluating. That’s short-circuit evaluation.
As you can probably imagine, a similar argument can be made for the OR operator. In that case, if the first value comes out as true, the whole expression is true. No need to worry about checking if the second expression is also true.
Why Take Advantage of Short-Circuit Evaluation?
Hopefully, through the example above, you know of at least one use for short-circuit evaluation. To be more explicit, we use the trick to avoid crashing our program if some condition ends up false. Another common place this happens is with null checks. If you want to run a method on an object but not sure if it’s null, use short-circuit evaluation:
if car is not None and car.is_totaled(): print("At least I can scrap it for parts!")
Sometimes, you’re less concerned about crashes and more concerned about time consuming operations:
if not is_even(num) and not is_prime(num): print("We have a composite number!")
In this example, we check if a number is not even and not prime, but we’re strategic with which one we check first. Checking if a number is even is usually an inexpensive task. On the other hand, checking if a number is prime is a bit more time consuming. To save time, we avoid checking if the even numbers are prime.
Surely, there are other use cases, but my general approach to short-circuit evaluation is to protect myself from errors and speed up my programs. As an added bonus, the trick tends to clean up code a bit. No more pesky nesting: only boolean expressions.
At any rate, hopefully this helped you make sense of the concept of short-circuit evaluation. It’s a handy trick that I recommend using to simplify your code when possible. If you liked this trick and want to learn more coding tricks, check out some of these related posts:
- Dump Java: Life Is Just Better in Python
- Stop Using Flags to Control Your Loops
- Practice Your Coding Skills With the Sample Programs Template
- Control Flow Syntax and Design in Java
While you’re hear, I’d appreciate it if you took some time to support the site. If not, no worries! I’ll see you next time.
Recent Posts
While creating some of the other early articles in this series, I had a realization: something even more fundamental than loops and if statements is the condition. As a result, I figured we could...
Today, we're expanding our concept map with the concept of loops in Python! Unless you're a complete beginner, you probably know a thing or two about loops, but maybe I can teach you something new.