Mojo basics 🔥
Advent of Code is an annual series of programming puzzles released every December. Many puzzles share a common format: grab some data, loop over it, evaluate a pattern, and compute the output. This example challenge follows the same steps. The toolkit demonstrated to solve this challenge will help you hit the ground running on any Advent of Code puzzle you pick up.
At the North Pole, a set of workshop sensors has recorded one week of temperature readings. Before anyone can spot problems in the heating system, they need a program that can inspect the data.
Hello Mojo
Create analyzer.mojo in your favorite IDE or editor.
Start with a message that identifies your new tool:
def main():
print("North Pole Temperature Analyzer")
Run it:
mojo analyzer.mojo
Takeaways
- If you see “North Pole Temperature Analyzer”, your setup works.
- All Mojo executables use
main()as their entry point.
Variables and data
The workshop sensors have already collected four temperature readings. Store them in a list so your analyzer can work with them.
Update your file:
def main():
print("North Pole Temperature Analyzer")
# Square brackets tell Mojo the `List` type at compile time
var temps: List[Float64] = [-20.5, -22.3, -19.8, -25.1]
print(t"Recorded {len(temps)} temperatures") # TStrings are templates
Mojo’s T-String adds string interpolation, building a string template with braces. Mojo replaces the braced expressions with their values. T-String braces can contain expressions, including function calls, arithmetic, and values, not just variable names.
Loops
Before looking for patterns, print each day’s reading so you can see what the sensors recorded.
Add this code to main() under the existing print statement:
for index in range(len(temps)): # The range is [0, len(temps))
print(t" Day {index + 1}: {temps[index]}°C")
You can rework the above to improve readability:
for index, temp in enumerate(temps):
print(t" Day {index + 1}: {temp}°C")
Takeaways
- The
range()function is yourforloop workhorse. - Mojo also has
whileloops. They work exactly as you expect. - All Mojo loops support
breakto stop the loop,continueto start the next iteration, andreturnto leave the loop and return from the function.
Functions
The workshop receives new readings every day. Put the average calculation in a function so the analyzer can reuse it.
Add this function above main():
def calculate_average(temps: List[Float64]) -> Float64:
var total: Float64 = 0.0
var count: Int = 0
for temp in temps: # Iterate over collection values
total += temp
count += 1
return total / Float64(count)
Add this code to the end of main() to call the function:
var avg = calculate_average(temps)
print(t"Average: {round(avg, 2)}°C") # Average: -21.92°C
Takeaways
- Place return types after arrow tokens.
- Like all compound statements,
calculate_average()needs a colon before its body. - Function bodies must use consistent indentation. Mojo convention uses four spaces.
- Use
round()to specify the number of digits after the decimal point.
Conditionals
The elves need more than a number. Classify the week’s average so they can quickly see whether the workshop was cool, comfortable, or hot.
Add this code to the end of main():
if avg > -20.0:
print("Status: Hot week")
elif avg > -25.0:
print("Status: Comfortable week")
else:
print("Status: Cool week")
Takeaways
- Add as many
elifclauses as you need, from zero to many. - Mojo also has a ternary expression:
value if condition else alt_value.
Raise errors
Sometimes a sensor fails and produces no readings. Your analyzer should notice instead of trying to calculate an average from an empty list.
Update calculate_average() so the function can raise an error:
def calculate_average(temps: List[Float64]) raises -> Float64:
if len(temps) == 0:
raise Error("No temperature data")
var total: Float64 = 0.0
var count: Int = 0
for temp in temps:
total += temp
count += 1
return total / Float64(count)
What changed:
- You added
raisesbefore the return arrow. - You added an empty-list check.
- You raised an
Errorwhen the list contained no readings.
Handle errors
A missing sensor report shouldn’t leave the elves wondering what happened. Catch the error and print a useful message.
Return to main() and wrap the average calculation and classification in
try and except:
try:
var avg = calculate_average(temps)
print(t"Average: {round(avg, 2)}°C") # Average: -21.92°C
if avg > -20.0:
print("Status: Hot week")
elif avg > -25.0:
print("Status: Comfortable week")
else:
print("Status: Cool week")
except e:
print("Error:", e)
To test the error, replace temps with List[Float64](). This constructs
an empty list of Float64 values.
Confirm that your program reports No temperature data, then restore the
original readings.
Final code
Your complete analyzer.mojo:
def calculate_average(temps: List[Float64]) raises -> Float64:
if len(temps) == 0:
raise Error("No temperature data")
var total: Float64 = 0.0
var count: Int = 0
for temp in temps:
total += temp
count += 1
return total / Float64(count)
def main():
print("North Pole Temperature Analyzer")
var temps: List[Float64] = [-20.5, -22.3, -19.8, -25.1]
print(t"Recorded {len(temps)} temperatures")
for index, temp in enumerate(temps):
print(t" Day {index + 1}: {temp}°C")
try:
var avg = calculate_average(temps)
print(t"Average: {round(avg, 2)}°C") # Average: -21.92°C
if avg > -20.0:
print("Status: Hot week")
elif avg > -25.0:
print("Status: Comfortable week")
else:
print("Status: Cool week")
except e:
print("Error:", e)
The analyzer can now store sensor readings, inspect them, calculate their average, classify the week, and report missing data.
The North Pole has its first working analysis tool.
Topics covered
Mojo variables, lists, loops, functions, conditionals, and error handling.