|
@@ -0,0 +1,120 @@
|
|
|
|
+//
|
|
|
|
+// The 'for' loop is not just limited to looping over one or two
|
|
|
|
+// items. Let's try an example with a whole bunch!
|
|
|
|
+//
|
|
|
|
+// But first, there's one last thing we've avoided mentioning
|
|
|
|
+// until now: The special range that leaves off the last value:
|
|
|
|
+//
|
|
|
|
+// for ( things, 0.. ) |t, i| { ... }
|
|
|
|
+//
|
|
|
|
+// That's how we tell Zig that we want to get a numeric value for
|
|
|
|
+// every item in "things", starting with 0.
|
|
|
|
+//
|
|
|
|
+// A nice feature of these index ranges is that you can have them
|
|
|
|
+// start with any number you choose. The first value of "i" in
|
|
|
|
+// this example will be 500, then 501, 502, etc.:
|
|
|
|
+//
|
|
|
|
+// for ( things, 500.. ) |t, i| { ... }
|
|
|
|
+//
|
|
|
|
+// Remember our RPG characters? They had the following
|
|
|
|
+// properties, which we stored in a struct type:
|
|
|
|
+//
|
|
|
|
+// class
|
|
|
|
+// gold
|
|
|
|
+// experience
|
|
|
|
+//
|
|
|
|
+// What we're going to do now is store the same RPG character
|
|
|
|
+// data, but in a separate array for each property.
|
|
|
|
+//
|
|
|
|
+// It might look a little awkward, but let's bear with it.
|
|
|
|
+//
|
|
|
|
+// We've started writing a program to print a numbered list of
|
|
|
|
+// characters with each of their properties, but it needs a
|
|
|
|
+// little help:
|
|
|
|
+//
|
|
|
|
+const std = @import("std");
|
|
|
|
+const print = std.debug.print;
|
|
|
|
+
|
|
|
|
+// This is the same character class enum we've seen before.
|
|
|
|
+const Class = enum {
|
|
|
|
+ wizard,
|
|
|
|
+ thief,
|
|
|
|
+ bard,
|
|
|
|
+ warrior,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+pub fn main() void {
|
|
|
|
+ // Here are the three "property" arrays:
|
|
|
|
+ const classes = [4]Class{ .wizard, .bard, .bard, .warrior };
|
|
|
|
+ const gold = [4]u16{ 25, 11, 5, 7392 };
|
|
|
|
+ const experience = [4]u8{ 40, 17, 55, 21 };
|
|
|
|
+
|
|
|
|
+ // We would like to number our list starting with 1, not 0.
|
|
|
|
+ // How do we do that?
|
|
|
|
+ for (classes, gold, experience, ???) |c, g, e, i| {
|
|
|
|
+ const class_name = switch (c) {
|
|
|
|
+ .wizard => "Wizard",
|
|
|
|
+ .thief => "Thief",
|
|
|
|
+ .bard => "Bard",
|
|
|
|
+ .warrior => "Warrior",
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ std.debug.print("{d}. {s} (Gold: {d}, XP: {d})\n", .{
|
|
|
|
+ i,
|
|
|
|
+ class_name,
|
|
|
|
+ g,
|
|
|
|
+ e,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+//
|
|
|
|
+// By the way, storing our character data in arrays like this
|
|
|
|
+// isn't *just* a silly way to demonstrate multi-object 'for'
|
|
|
|
+// loops.
|
|
|
|
+//
|
|
|
|
+// It's *also* a silly way to introduce a concept called
|
|
|
|
+// "data-oriented design".
|
|
|
|
+//
|
|
|
|
+// Let's use a metaphor to build up an intuition for what this is
|
|
|
|
+// all about:
|
|
|
|
+//
|
|
|
|
+// Let's say you've been tasked with grabbing three glass
|
|
|
|
+// marbles, three spoons, and three feathers from a bucket. But
|
|
|
|
+// you can't use your hands to grab them. Instead, you have a
|
|
|
|
+// special marble scoop, spoon magnet, and feather tongs to grab
|
|
|
|
+// each type of object.
|
|
|
|
+//
|
|
|
|
+// Now, would you rather have:
|
|
|
|
+//
|
|
|
|
+// A. The items layered so you have to pick up one marble, then
|
|
|
|
+// one spoon, then one feather?
|
|
|
|
+//
|
|
|
|
+// OR
|
|
|
|
+//
|
|
|
|
+// B. The items separated by type so you can pick up all of the
|
|
|
|
+// marbles at once, then all the spoons, then all of the
|
|
|
|
+// feathers?
|
|
|
|
+//
|
|
|
|
+// If this metaphor is working, hopefully it's clear that the 'B'
|
|
|
|
+// option would be much more efficient.
|
|
|
|
+//
|
|
|
|
+// Well, it probably comes as little surprise that storing and
|
|
|
|
+// using data in a sequential and uniform fashion is also more
|
|
|
|
+// efficient for modern CPUs.
|
|
|
|
+//
|
|
|
|
+// Decades of OOP practices have steered people towards grouping
|
|
|
|
+// different data types together into "objects" with the hope
|
|
|
|
+// that it would be friendlier to the human mind. But
|
|
|
|
+// data-oriented design groups data in a way that is more
|
|
|
|
+// efficient for the computer.
|
|
|
|
+//
|
|
|
|
+// In Zig terminology, the difference in groupings is sometimes
|
|
|
|
+// known as "Array of Structs" (AoS) versus "Struct of Arrays"
|
|
|
|
+// (SoA).
|
|
|
|
+//
|
|
|
|
+// To envision these two designs in action, imagine an array of
|
|
|
|
+// RPG character structs, each containing three different data
|
|
|
|
+// types (AoS) versus a single RPG character struct containing
|
|
|
|
+// three arrays of one data type each, like those in the exercise
|
|
|
|
+// above (SoA).
|
|
|
|
+//
|