From e2f3a5e5192ba9e28d51c39c258a37003cc9acb0 Mon Sep 17 00:00:00 2001 From: Dave Gauer Date: Tue, 2 May 2023 08:26:32 -0400 Subject: [PATCH] Added Ex 101 "for loops part 5" (Closes #271) Also gave a shot at explaining data-oriented design, a Zig "hot topic" ever since the red Hawaiian shirt talk(s). --- build.zig | 4 ++ exercises/101_for5.zig | 120 +++++++++++++++++++++++++++++++++ patches/patches/101_for5.patch | 4 ++ 3 files changed, 128 insertions(+) create mode 100644 exercises/101_for5.zig create mode 100644 patches/patches/101_for5.patch diff --git a/build.zig b/build.zig index 38890c2..1b4855d 100644 --- a/build.zig +++ b/build.zig @@ -1076,6 +1076,10 @@ const exercises = [_]Exercise{ .main_file = "100_for4.zig", .output = "Arrays match!", }, + .{ + .main_file = "101_for5.zig", + .output = "1. Wizard (Gold: 25, XP: 40)\n2. Bard (Gold: 11, XP: 17)\n3. Bard (Gold: 5, XP: 55)\n4. Warrior (Gold: 7392, XP: 21)", + }, .{ .main_file = "999_the_end.zig", .output = "\nThis is the end for now!\nWe hope you had fun and were able to learn a lot, so visit us again when the next exercises are available.", diff --git a/exercises/101_for5.zig b/exercises/101_for5.zig new file mode 100644 index 0000000..3861417 --- /dev/null +++ b/exercises/101_for5.zig @@ -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). +// diff --git a/patches/patches/101_for5.patch b/patches/patches/101_for5.patch new file mode 100644 index 0000000..0466cc8 --- /dev/null +++ b/patches/patches/101_for5.patch @@ -0,0 +1,4 @@ +54c54 +< for (classes, gold, experience, ???) |c, g, e, i| { +--- +> for (classes, gold, experience, 1..) |c, g, e, i| {