Просмотр исходного кода

Added story/explanation to new ex. 092

Dave Gauer 2 лет назад
Родитель
Сommit
662086cb89
3 измененных файлов с 87 добавлено и 28 удалено
  1. 1 1
      build.zig
  2. 83 24
      exercises/092_interfaces.zig
  3. 3 3
      patches/patches/092_interfaces.patch

+ 1 - 1
build.zig

@@ -462,7 +462,7 @@ const exercises = [_]Exercise{
     // },
     .{
         .main_file = "092_interfaces.zig",
-        .output = "Daily insect report:\nAnt is alive.\nBee visited 17 flowers.\nGrasshopper hopped 32 m.",
+        .output = "Daily Insect Report:\nAnt is alive.\nBee visited 17 flowers.\nGrasshopper hopped 32 meters.",
     },
     .{
         .main_file = "999_the_end.zig",

+ 83 - 24
exercises/092_interfaces.zig

@@ -1,7 +1,55 @@
 //
-// Remeber excerices 55-57 with tagged unions.
+// Remember our ant and bee simulator constructed with unions
+// back in exercises 55 and 56? There, we demonstrated that
+// unions allow us to treat different data types in a uniform
+// manner.
 //
-// (story/explanation from Dave)
+// One neat feature was using tagged unions to create a single
+// function to print a status for ants *or* bees by switching:
+//
+//   switch (insect) {
+//      .still_alive => ...      // (print ant stuff)
+//      .flowers_visited => ...  // (print bee stuff)
+//   }
+//
+// Well, that simulation was running just fine until a new insect
+// arrived in the virtual garden, a grasshopper!
+//
+// Doctor Zoraptera started to add grasshopper code to the
+// program, but then she backed away from her keyboard with an
+// angry hissing sound. She had realized that having code for
+// each insect in one place and code to print each insect in
+// another place was going to become unpleasant to maintain when
+// the simulation expanded to hundreds of different insects.
+//
+// Thankfully, Zig has another comptime feature we can use
+// to get out of this dilema called the 'inline else'.
+//
+// We can replace this redundant code:
+//
+//   switch (thing) {
+//       .a => |a| special(a),
+//       .b => |b| normal(b),
+//       .c => |c| normal(c),
+//       .d => |d| normal(d),
+//       .e => |e| normal(e),
+//       ...
+//   }
+//
+// With:
+//
+//   switch (thing) {
+//       .a => |a| special(a),
+//       inline else |t| => normal(t),
+//   }
+//
+// We can have special handling of some cases and then Zig
+// handles the rest of the matches for us.
+//
+// With this feature, you decide to make an Insect union with a
+// single uniform 'print()' function. All of the insects can
+// then be responsible for printing themselves. And Doctor
+// Zoraptera can calm down and stop gnawing on the furniture.
 //
 const std = @import("std");
 
@@ -9,7 +57,7 @@ const Ant = struct {
     still_alive: bool,
 
     pub fn print(self: Ant) void {
-        std.debug.print("Ant is {s}.\n", .{if (self.still_alive) "alive" else "death"});
+        std.debug.print("Ant is {s}.\n", .{if (self.still_alive) "alive" else "dead"});
     }
 };
 
@@ -21,11 +69,13 @@ const Bee = struct {
     }
 };
 
+// Here's the new grasshopper. Notice how we've also added print
+// methods to each insect.
 const Grasshopper = struct {
     distance_hopped: u16,
 
     pub fn print(self: Grasshopper) void {
-        std.debug.print("Grasshopper hopped {} m.\n", .{self.distance_hopped});
+        std.debug.print("Grasshopper hopped {} meters.\n", .{self.distance_hopped});
     }
 };
 
@@ -34,6 +84,10 @@ const Insect = union(enum) {
     bee: Bee,
     grasshopper: Grasshopper,
 
+    // Thanks to 'inline else', we can think of this print() as
+    // being an interface method. Any member of this union with
+    // with a print() method can be treated uniformly by outside
+    // code without needing to know any other details. Cool!
     pub fn print(self: Insect) void {
         switch (self) {
             inline else => |case| return case.print(),
@@ -42,27 +96,32 @@ const Insect = union(enum) {
 };
 
 pub fn main() !void {
-    var my_insects = [_]Insect{ Insect{
-        .ant = Ant{ .still_alive = true },
-    }, Insect{
-        .bee = Bee{ .flowers_visited = 17 },
-    }, Insect{
-        .grasshopper = Grasshopper{ .distance_hopped = 32 },
-    } };
-
-    // The daily situation report, what's going on in the garden
-    try dailyReport("what is here the right parameter?");
-}
+    var my_insects = [_]Insect{
+        Insect{ .ant = Ant{ .still_alive = true } },
+        Insect{ .bee = Bee{ .flowers_visited = 17 } },
+        Insect{ .grasshopper = Grasshopper{ .distance_hopped = 32 }, },
+    };
 
-// Through the interface we can keep a list of various objects
-// (in this case the insects of our garden) and even pass them
-// to a function without having to know the specific properties
-// of each or the object itself. This is really cool!
-fn dailyReport(insectReport: []Insect) !void {
-    std.debug.print("Daily insect report:\n", .{});
-    for (insectReport) |insect| {
-        insect.print();
+    std.debug.print("Daily Insect Report:\n", .{});
+    for (my_insects) |insect| {
+        // Almost done! We want to print() each insect with a
+        // single method call here.
+        ???
     }
 }
 
-// Interfaces... (explanation from Dave)
+// Our print() method in the Insect union above demonstrates
+// something very similar to the object-oriented concept of an
+// abstract data type. That is, the Insect type doesn't contain
+// the underlying data, and the print() function doesn't
+// actually do the printing.
+//
+// The point of an interface is to support generic programming:
+// the ability to treat different things as if they were the
+// same to cut down on clutter and conceptual complexity.
+//
+// The Daily Insect Report doesn't need to worry about *which*
+// insects are in the report - they all print the same way via
+// the interface!
+//
+// Doctor Zoraptera loves it.

+ 3 - 3
patches/patches/092_interfaces.patch

@@ -1,4 +1,4 @@
-54c54
-<     try dailyReport("what is here the right parameter?");
+109c109
+<         ???
 ---
->     try dailyReport(&my_insects);
+>         insect.print();