diff options
Diffstat (limited to 'exercises')
| -rw-r--r-- | exercises/015_for.zig | 6 | ||||
| -rw-r--r-- | exercises/016_for2.zig | 6 | ||||
| -rw-r--r-- | exercises/060_floats.zig | 19 | ||||
| -rw-r--r-- | exercises/095_for3.zig | 73 | ||||
| -rw-r--r-- | exercises/095_for_loops.zig | 64 | ||||
| -rw-r--r-- | exercises/100_for4.zig | 62 | ||||
| -rw-r--r-- | exercises/101_for5.zig | 120 |
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). +// |
