summaryrefslogtreecommitdiff
path: root/exercises
diff options
context:
space:
mode:
Diffstat (limited to 'exercises')
-rw-r--r--exercises/015_for.zig6
-rw-r--r--exercises/016_for2.zig6
-rw-r--r--exercises/060_floats.zig19
-rw-r--r--exercises/095_for3.zig73
-rw-r--r--exercises/095_for_loops.zig64
-rw-r--r--exercises/100_for4.zig62
-rw-r--r--exercises/101_for5.zig120
7 files changed, 277 insertions, 73 deletions
diff --git a/exercises/015_for.zig b/exercises/015_for.zig
index 4c87a05..0ee8e7d 100644
--- a/exercises/015_for.zig
+++ b/exercises/015_for.zig
@@ -23,5 +23,9 @@ pub fn main() void {
std.debug.print("The End.\n", .{});
}
-// Note that "for" loops also work on things called "slices"
+// Note that 'for' loops also work on things called "slices"
// which we'll see later.
+//
+// Also note that 'for' loops have recently become more flexible
+// and powerful (two years after this exercise was written).
+// More about that in a moment.
diff --git a/exercises/016_for2.zig b/exercises/016_for2.zig
index 4a8d09c..ea1e6ca 100644
--- a/exercises/016_for2.zig
+++ b/exercises/016_for2.zig
@@ -35,3 +35,9 @@ pub fn main() void {
std.debug.print("The value of bits '1101': {}.\n", .{value});
}
+//
+// As mentioned in the previous exercise, 'for' loops have gained
+// additional flexibility since these early exercises were
+// written. As we'll see in later exercises, the above syntax for
+// capturing the index is part of a more general ability. Hang in
+// there!
diff --git a/exercises/060_floats.zig b/exercises/060_floats.zig
index 8ba51db..1320171 100644
--- a/exercises/060_floats.zig
+++ b/exercises/060_floats.zig
@@ -1,10 +1,11 @@
//
// Zig has support for IEEE-754 floating-point numbers in these
// specific sizes: f16, f32, f64, f80, and f128. Floating point
-// literals may be written in scientific notation:
+// literals may be written in the same ways as integers but also
+// in scientific notation:
//
-// const a1: f32 = 1200.0; // 1,200
-// const a2: f32 = 1.2e+3; // 1,200
+// const a1: f32 = 1200; // 1,200
+// const a2: f32 = 1.2e+3; // 1,200
// const b1: f32 = -500_000.0; // -500,000
// const b2: f32 = -5.0e+5; // -500,000
//
@@ -22,12 +23,14 @@
// const pi: f16 = 3.1415926535; // rounds to 3.140625
// const av: f16 = 6.02214076e+23; // Avogadro's inf(inity)!
//
-// A float literal has a decimal point. When performing math
-// operations with numeric literals, ensure the types match. Zig
-// does not perform unsafe type coercions behind your back:
+// When performing math operations with numeric literals, ensure
+// the types match. Zig does not perform unsafe type coercions
+// behind your back:
//
-// var foo: f16 = 13.5 * 5; // ERROR!
-// var foo: f16 = 13.5 * 5.0; // No problem, both are floats
+// var foo: f16 = 5; // NO ERROR
+//
+// var foo: u16 = 5; // A literal of a different type
+// var bar: f16 = foo; // ERROR
//
// Please fix the two float problems with this program and
// display the result as a whole number.
diff --git a/exercises/095_for3.zig b/exercises/095_for3.zig
new file mode 100644
index 0000000..e4c4662
--- /dev/null
+++ b/exercises/095_for3.zig
@@ -0,0 +1,73 @@
+//
+// The Zig language is in rapid development and continuously
+// improves the language constructs. Ziglings evolves with it.
+//
+// Until version 0.11, Zig's 'for' loops did not directly
+// replicate the functionality of the C-style: "for(a;b;c)"
+// which are so well suited for iterating over a numeric
+// sequence.
+//
+// Instead, 'while' loops with counters clumsily stood in their
+// place:
+//
+// var i: usize = 0;
+// while (i < 10) : (i += 1) {
+// // Here variable 'i' will have each value 0 to 9.
+// }
+//
+// But here we are in the glorious future and Zig's 'for' loops
+// can now take this form:
+//
+// for (0..10) |i| {
+// // Here variable 'i' will have each value 0 to 9.
+// }
+//
+// The key to understanding this example is to know that '0..9'
+// uses the new range syntax:
+//
+// 0..10 is a range from 0 to 9
+// 1..4 is a range from 1 to 3
+//
+// At the moment, ranges are only supported in 'for' loops.
+//
+// Perhaps you recall Exercise 13? We were printing a numeric
+// sequence like so:
+//
+// var n: u32 = 1;
+//
+// // I want to print every number between 1 and 20 that is NOT
+// // divisible by 3 or 5.
+// while (n <= 20) : (n += 1) {
+// // The '%' symbol is the "modulo" operator and it
+// // returns the remainder after division.
+// if (n % 3 == 0) continue;
+// if (n % 5 == 0) continue;
+// std.debug.print("{} ", .{n});
+// }
+//
+// Let's try out the new form of 'for' to re-implement that
+// exercise:
+//
+const std = @import("std");
+
+pub fn main() void {
+
+ // I want to print every number between 1 and 20 that is NOT
+ // divisible by 3 or 5.
+ for (???) |n| {
+
+ // The '%' symbol is the "modulo" operator and it
+ // returns the remainder after division.
+ if (n % 3 == 0) continue;
+ if (n % 5 == 0) continue;
+ std.debug.print("{} ", .{n});
+ }
+
+ std.debug.print("\n", .{});
+}
+//
+// That's a bit nicer, right?
+//
+// Of course, both 'while' and 'for' have different advantages.
+// Exercises 11, 12, and 14 would NOT be simplified by switching
+// a 'while' for a 'for'.
diff --git a/exercises/095_for_loops.zig b/exercises/095_for_loops.zig
deleted file mode 100644
index b437946..0000000
--- a/exercises/095_for_loops.zig
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// The Zig language is in rapid development and continuously improves
-// the language constructs steadily.
-//
-// Since version 0.11, the "for-loops" widely used in other languages
-// such as C, e.g. "for (int i = 0; i < 10..." can now also be formed
-// similarly in Zig, which previously required a "while" construct.
-// Similar in this case actually means better, just as Zig generally
-// tries to make everything simple and "better".
-//
-// These new "for-loops" look like the following in Zig:
-//
-// for (0..10) |idx| {
-// // In this case 'idx' takes all values from 0 to 9.
-// }
-//
-// This is really simple and can replace the previous, somewhat bulky:
-//
-// var idx: usize = 0;
-// while (idx < 10) : (idx += 1) {
-// // Again, idx takes all values from 0 to 9.
-// }
-//
-// This would also simplify exercise 13, for example.
-// The best way to try this out is to use this exercise, which in the
-// original looks like this:
-//
-// ...
-// var n: u32 = 1;
-//
-// // I want to print every number between 1 and 20 that is NOT
-// // divisible by 3 or 5.
-// while (n <= 20) : (n += 1) {
-// // The '%' symbol is the "modulo" operator and it
-// // returns the remainder after division.
-// if (n % 3 == 0) continue;
-// if (n % 5 == 0) continue;
-// std.debug.print("{} ", .{n});
-// }
-// ...
-//
-const std = @import("std");
-
-// And now with the new "for-loop".
-pub fn main() void {
-
- // I want to print every number between 1 and 20 that is NOT
- // divisible by 3 or 5.
- for (???) |n| {
-
- // The '%' symbol is the "modulo" operator and it
- // returns the remainder after division.
- if (n % 3 == 0) continue;
- if (n % 5 == 0) continue;
- std.debug.print("{} ", .{n});
- }
-
- std.debug.print("\n", .{});
-}
-
-// Is actually a little easier. The interesting thing here is that the other
-// previous 'while' exercises (11,12, 14) cannot be simplified by this
-// new "for-loop". Therefore it is good to be able to use both variations
-// accordingly.
diff --git a/exercises/100_for4.zig b/exercises/100_for4.zig
new file mode 100644
index 0000000..e0fa602
--- /dev/null
+++ b/exercises/100_for4.zig
@@ -0,0 +1,62 @@
+//
+// We've seen that the 'for' loop can let us perform some action
+// for every item in an array or slice.
+//
+// More recently, we discovered that it supports ranges to
+// iterate over number sequences.
+//
+// This is part of a more general capability of the `for` loop:
+// looping over one or more "objects" where an object is an
+// array, slice, or range.
+//
+// In fact, we *did* use multiple objects way back in Exercise
+// 016 where we iterated over an array and also a numeric index.
+// It didn't always work exactly this way, so the exercise had to
+// be retroactively modified a little bit.
+//
+// for (bits, 0..) |bit, i| { ... }
+//
+// The general form of a 'for' loop with two lists is:
+//
+// for (list_a, list_b) |a, b| {
+// // Here we have the first item from list_a and list_b,
+// // then the second item from each, then the third and
+// // so forth...
+// }
+//
+// What's really beautiful about this is that we don't have to
+// keep track of an index or advancing a memory pointer for
+// *either* of these lists. That error-prone stuff is all taken
+// care of for us by the compiler.
+//
+// Below, we have a program that is supposed to compare two
+// arrays. Please make it work!
+//
+const std = @import("std");
+const print = std.debug.print;
+
+pub fn main() void {
+ const hex_nums = [_]u8{ 0xb, 0x2a, 0x77 };
+ const dec_nums = [_]u8{ 11, 42, 119 };
+
+ for (hex_nums, ???) |hn, ???| {
+ if (hn != dn) {
+ std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
+ return;
+ }
+ }
+
+ std.debug.print("Arrays match!\n", .{});
+}
+//
+// You are perhaps wondering what happens if one of the two lists
+// is longer than the other? Try it!
+//
+// By the way, congratulations for making it to Exercise 100!
+//
+// +-------------+
+// | Celebration |
+// | Area * * * |
+// +-------------+
+//
+// Please keep your celebrating within the area provided.
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).
+//