Reuse your code 🔥
By now, you’ve written code you shouldn’t have to write again. Pulling numbers from text, correcting readings, and summarizing values aren’t tied to one puzzle. They’re useful tools you can carry forward to the next one. Once a solution starts repeating itself, it’s a good candidate for a function or library.
In this chapter, you’ll move some of that working code into a reusable toolkit and call it from another program. You’ll build and link a Mojo library, define defaults that keep common calls simple, and combine small functions into a processing pipeline. The next time a puzzle needs the same machinery, you’ll already have it.
Create reusable libraries
By now you’ve pulled integers from text, corrected readings, and averaged them. You wrote each move inline. A function gives the move a name and a home, letting the next puzzle reuse it instead of retyping it.
Build a library
Create a folder called toolkit/.
In that folder, create __init__.mojo and add the extractor. There’s no
main() function, just the one extract_ints() function:
def extract_ints(text: String) raises -> List[Int]:
var nums = List[Int]()
var cur = String("")
for i in range(text.byte_length()):
var c = String(text[byte=i])
if c >= "0" and c <= "9":
cur += c
elif cur:
nums.append(Int(cur))
cur = String("")
if cur:
nums.append(Int(cur))
return nums^
Compile it with precompile. This builds toolkit.mojoc:
mojo precompile toolkit.mojo
Link a library
Now create test.mojo in your main folder, not inside toolkit:
from toolkit import extract_ints
def main():
var s = "a 1 b 2 c 3"
try:
var list = extract_ints(s)
print(list)
except e:
print(e)
Compile and run test.mojo:
mojo test.mojo toolkit.mojoc
Your app prints [1, 2, 3] using the function from your library.
Takeaways
- A library lets you write once and use often.
- The declarations in the library are your API contract.
extract_intspromises aList[Int]and hands ownership out with^.
Default arguments
An argument can carry a default, so callers skip it in the common case and override it when they need to. Station 3 reads high when the sun hits it midday, so correct readings taken in those hours. Add to your library:
def corrected(reading: Float64, hour: Int, offset: Float64 = 1.5) -> Float64:
# station 3 bakes in solar loading between 10:00 and 14:00
if 10 <= hour < 14:
return reading - offset
return reading
Call it from main() with the default, or name the argument to override it:
print(t"noon, default: {corrected(24.5, 12)}") # 23.0
print(t"noon, bigger: {corrected(24.5, 12, offset=2.0)}") # 22.5
Takeaways
offset: Float64 = 1.5gives the argument a default. Callers that omit supplying their ownoffsetuse this as their offset value.- Name an argument at the call site (
offset=2.0) to set it explicitly and skip the guesswork about argument order. - A default keeps the common call short.
Compose functions
Small functions snap together. Add one that averages a list, then run readings through correction and into the average. In the library:
def mean_of(values: List[Float64]) -> Float64:
var total = 0.0
for v in values:
total += v
return total / Float64(len(values))
In main(), correct each reading, then summarize the corrected set:
var raw: List[Float64] = [22.1, 24.5, 19.8]
var hours = [9, 12, 16]
var fixed = List[Float64]()
for r, h in zip(raw, hours):
fixed.append(corrected(r, h))
print(t"corrected: {fixed}") # [22.1, 23.0, 19.8]
print(t"mean after correction: {round(mean_of(fixed), 2)}") # 21.63
Takeaways
- Each function does one thing, so they read as a pipeline: parse, correct, summarize.
correctedruns per reading;mean_ofruns over the whole list. Small pieces, combined at the call site.
Final code
Your complete toolkit/__init__.mojo:
def extract_ints(text: String) raises -> List[Int]:
var nums = List[Int]()
var cur = String("")
for i in range(text.byte_length()):
var c = String(text[byte=i])
if c >= "0" and c <= "9":
cur += c
elif cur:
nums.append(Int(cur))
cur = String("")
if cur:
nums.append(Int(cur))
return nums^
def corrected(reading: Float64, hour: Int, offset: Float64 = 1.5) -> Float64:
# station 3 bakes in solar loading between 10:00 and 14:00
if 10 <= hour < 14:
return reading - offset
return reading
def mean_of(values: List[Float64]) -> Float64:
var total = 0.0
for v in values:
total += v
return total / Float64(len(values))
and:
from toolkit import extract_ints, corrected, mean_of
def main() raises:
print(t"reused: {extract_ints('readings: 3, 5, 8')}") # [3, 5, 8]
print(t"noon, default: {corrected(24.5, 12)}") # 23.0
print(t"noon, bigger: {corrected(24.5, 12, offset=2.0)}") # 22.5
var raw: List[Float64] = [22.1, 24.5, 19.8]
var hours = [9, 12, 16]
var fixed = List[Float64]()
for r, h in zip(raw, hours):
fixed.append(corrected(r, h))
print(t"corrected: {fixed}") # [22.1, 23.0, 19.8]
print(t"mean after correction: {round(mean_of(fixed), 2)}") # 21.63
Topics covered
Building a library and linking it, naming a move as a function, return-type
contracts, returning a List with ^, default arguments, keyword
arguments at the call site, and composing small functions into a pipeline.