Browse Source

Added ex063 labels

Dave Gauer 4 years ago
parent
commit
cc269968ea
4 changed files with 156 additions and 5 deletions
  1. 6 5
      README.md
  2. 4 0
      build.zig
  3. 140 0
      exercises/063_labels.zig
  4. 6 0
      patches/patches/063_labels.patch

+ 6 - 5
README.md

@@ -133,11 +133,12 @@ Planned exercises:
 * [x] Slices
 * [x] Many-item pointers
 * [x] Unions
-* [ ] Numeric types (integers, floats)
-* [ ] Labelled blocks and loops
-* [ ] Loops as expressions
-* [ ] Comptime
-* [ ] Inline loops (how to DEMO this?)
+* [x] Numeric types (integers, floats)
+* [x] Labelled blocks and loops
+* [x] Loops as expressions
+* [ ] Builtins
+* [ ] Comptime (!)
+* [ ] Inline loops
 * [ ] Anonymous structs
 * [ ] Sentinel termination
 * [ ] Vectors

+ 4 - 0
build.zig

@@ -318,6 +318,10 @@ const exercises = [_]Exercise{
         .main_file = "062_loop_expressions.zig",
         .output = "Current language: Zig",
     },
+    .{
+        .main_file = "063_labels.zig",
+        .output = "Enjoy your Cheesy Chili!",
+    },
 };
 
 /// Check the zig version to make sure it can compile the examples properly.

+ 140 - 0
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!

+ 6 - 0
patches/patches/063_labels.patch

@@ -0,0 +1,6 @@
+132,133c131,132
+<         break;
+<     };
+---
+>         break food;
+>     } else menu[0];