aboutsummaryrefslogtreecommitdiff
path: root/exercises/063_labels.zig
diff options
context:
space:
mode:
Diffstat (limited to 'exercises/063_labels.zig')
-rw-r--r--exercises/063_labels.zig140
1 files changed, 140 insertions, 0 deletions
diff --git a/exercises/063_labels.zig b/exercises/063_labels.zig
new file mode 100644
index 0000000..cdde229
--- /dev/null
+++ b/exercises/063_labels.zig
@@ -0,0 +1,140 @@
+//
+// Loop bodies are blocks, which are also expressions. We've seen
+// how they can be used to evaluate and return values. To further
+// expand on this concept, it turns out we can also give names to
+// blocks by applying a 'label':
+//
+// my_label: { ... }
+//
+// Once you give a block a label, you can use 'break' to exit
+// from that block.
+//
+// outer_block: { // outer block
+// while (true) { // inner block
+// break :outer_block;
+// }
+// unreachable;
+// }
+//
+// As we've just learned, you can return a value using a break
+// statement. Does that mean you can return a value from any
+// labeled block? Yes it does!
+//
+// const foo = make_five: {
+// const five = 1 + 1 + 1 + 1 + 1;
+// break :make_five five;
+// };
+//
+// Labels can also be used with loops. Being able to break out of
+// nested loops at a specific level is one of those things that
+// you won't use every day, but when the time comes, it's
+// incredibly convenient. Being able to return a value from an
+// inner loop is sometimes so handy, it almost feels like cheating
+// (and can help you avoid creating a lot of temporary variables).
+//
+// const bar: u8 = two_loop: while (true) {
+// while (true) {
+// break :two_loop 2;
+// }
+// } else 0;
+//
+// In the above example, the break exits from the outer loop
+// labeled "two_loop" and returns the value 2. The else clause is
+// attached to the outer two_loop and would be evaluated if the
+// loop somehow ended without the break having been called.
+//
+// Finally, you can also use block labels with the 'continue'
+// statement:
+//
+// my_while: while (true) {
+// continue :my_while;
+// }
+//
+const print = @import("std").debug.print;
+
+// As mentioned before, we'll soon understand why these two
+// numbers don't need explicit types. Hang in there!
+const ingredients = 4;
+const foods = 4;
+
+const Food = struct {
+ name: []const u8,
+ requires: [ingredients]bool,
+};
+
+// Chili Macaroni Tomato Sauce Cheese
+// ------------------------------------------------------
+// Mac & Cheese x x
+// Chili Mac x x
+// Pasta x x
+// Cheesy Chili x x
+// ------------------------------------------------------
+
+const menu: [foods]Food = [_]Food{
+ Food{
+ .name = "Mac & Cheese",
+ .requires = [ingredients]bool{ false, true, false, true },
+ },
+ Food{
+ .name = "Chili Mac",
+ .requires = [ingredients]bool{ true, true, false, false },
+ },
+ Food{
+ .name = "Pasta",
+ .requires = [ingredients]bool{ false, true, true, false },
+ },
+ Food{
+ .name = "Cheesy Chili",
+ .requires = [ingredients]bool{ true, false, false, true },
+ },
+};
+
+pub fn main() void {
+ // Welcome to Cafeteria USA! Choose your favorite ingredients
+ // and we'll produce a delicious meal.
+ //
+ // Cafeteria Customer Note: Not all ingredient combinations
+ // make a meal. The default meal is macaroni and cheese.
+ //
+ // Software Developer Note: Hard-coding the ingredient
+ // numbers (based on array position) will be fine for our
+ // tiny example, but it would be downright criminal in a real
+ // application!
+ const wanted_ingredients = [_]u8{ 0, 3 }; // Chili, Cheese
+
+ // Look at each Food on the menu...
+ var meal = food_loop: for (menu) |food| {
+
+ // Now look at each required ingredient for the Food...
+ for (food.requires) |required, required_ingredient| {
+
+ // This ingredient isn't required, so skip it.
+ if (!required) continue;
+
+ // See if the customer wanted this ingredient.
+ // (Remember that want_it will be the index number of
+ // the ingredient based on its position in the
+ // required ingredient list for each food.)
+ var found = for (wanted_ingredients) |want_it| {
+ if (required_ingredient == want_it) break true;
+ } else false;
+
+ // We did not find this required ingredient, so we
+ // can't make this Food. Continue the outer loop.
+ if (!found) continue :food_loop;
+ }
+
+ // If we get this far, the required ingredients were all
+ // wanted for this Food.
+ //
+ // Please return this Food from the loop.
+ break;
+ };
+ // ^ Oops! We forgot to return Mac & Cheese as the default
+ // Food when the requested ingredients aren't found.
+
+ print("Enjoy your {s}!\n", .{meal.name});
+}
+
+// Challenge: You can also do away with the 'found' variable in
+// the inner loop. See if you can figure out how to do that!