diff --git a/README.md b/README.md index a6792fd..41083f1 100644 --- a/README.md +++ b/README.md @@ -146,8 +146,8 @@ Core Language * [x] Builtins * [x] Inline loops * [x] Comptime +* [x] Sentinel termination * [ ] Anonymous structs -* [ ] Sentinel termination * [ ] Suspend / Resume * [ ] Async / Await * [ ] Nosuspend diff --git a/build.zig b/build.zig index f427b98..2489e2a 100644 --- a/build.zig +++ b/build.zig @@ -379,6 +379,10 @@ const exercises = [_]Exercise{ .output = "Archer's Point--2->Bridge--1->Dogwood Grove--3->Cottage--2->East Pond--1->Fox Pond", .hint = "Roll up those sleeves. You get to WRITE some code for this one.", }, + .{ + .main_file = "076_sentinels.zig", + .output = "Array:123056. Many-pointer:123.", + }, }; /// Check the zig version to make sure it can compile the examples properly. diff --git a/exercises/076_sentinels.zig b/exercises/076_sentinels.zig new file mode 100644 index 0000000..4cd25e9 --- /dev/null +++ b/exercises/076_sentinels.zig @@ -0,0 +1,121 @@ +// +// A sentinel value indicates the end of data. Let's imagine a +// sequence of lowercase letters where uppercase 'S' is the +// sentinel, indicating the end of the sequence: +// +// abcdefS +// +// If our sequence also allows for uppercase letters, 'S' would +// make a terrible sentinel since it could no longer be a regular +// value in the sequence: +// +// abcdQRST +// ^-- Oops! The last letter in the sequence is R! +// +// A popular choice for indicating the end of a string is the +// value 0. ASCII and Unicode call this the "Null Character". +// +// Zig supports sentinel-terminated arrays, slices, and pointers: +// +// const a: [4:0]u32 = [4:0]u32{1, 2, 3, 4}; +// const b: [:0]const u32 = &[4:0]u32{1, 2, 3, 4}; +// const c: [*:0]const u32 = &[4:0]u32{1, 2, 3, 4}; +// +// Array 'a' stores 5 u32 values, the last of which is 0. +// However the compiler takes care of this housekeeping detail +// for you. You can treat 'a' as a normal array with just 4 +// items. +// +// Slice 'b' is only allowed to point to zero-terminated arrays +// but otherwise works just like a normal slice. +// +// Pointer 'c' is exactly like the many-pointers we learned about +// in exercise 054, but it is guaranteed to end in 0. Because of +// this guarantee, we can safely find the end of this +// many-pointer without knowing its length. (We CAN'T do that +// with regular many-pointers!). +// +const print = @import("std").debug.print; + +pub fn main() void { + // Here's a zero-terminated array of u32 values: + var nums = [_:0]u32{ 1, 2, 3, 4, 5, 6 }; + + // And here's a zero-terminated many-pointer: + var ptr: [*:0]u32 = &nums; + + // For fun, let's replace the value at position 3 with the + // sentinel value 0. This seems kind of naughty. + nums[3] = 0; + + // So now we have a zero-terminated array and a many-pointer + // that reference the same data: a sequence of numbers that + // both ends in and CONTAINS the sentinal value. + // + // Attempting to loop through and print both of these should + // demonstrate how they are similar and different. + // + // (It turns out that the array prints completely, including + // the sentinel 0 in the middle. The many-pointer must stop + // at the first sentinel value. The difference is simply that + // arrays have a known length and many-pointers don't.) + printSequence(nums); + printSequence(ptr); + + print("\n", .{}); +} + +// Here's our generic sequence printing function. It's nearly +// complete, but there are a couple missing bits. Please fix +// them! +fn printSequence(my_seq: anytype) void { + const my_type = @typeInfo(@TypeOf(my_seq)); + + // The TypeInfo contained in my_type is a union. We use a + // switch to handle printing the Array or Pointer fields, + // depending on which type of my_seq was passed in: + switch (my_type) { + .Array => { + print("Array:", .{}); + + // Loop through the items in my_seq. + for (???) |s| { + print("{}", .{s}); + } + }, + .Pointer => { + // Check this out - it's pretty cool: + const my_sentinel = my_type.Pointer.sentinel; + print("Many-pointer:", .{}); + + // Loop through the items in my_seq until we hit the + // sentinel value. + var i: usize = 0; + while (??? != my_sentinel) { + print("{}", .{my_seq[i]}); + i += 1; + } + }, + else => unreachable, + } + print(". ", .{}); +} +// +// ------------------------------------------------------------ +// TOP SECRET TOP SECRET TOP SECRET TOP SECRET TOP SECRET +// ------------------------------------------------------------ +// +// Are you ready for the THE TRUTH about Zig string literals? +// +// You've earned it. Here it is: +// +// @TypeOf("foo") == *const [3:0]u8 +// +// Zig's string literals are constant pointers to zero-terminated +// (or "null-terminated") arrays of u8. +// +// Now you know. Welcome to the secret club! +// +// ------------------------------------------------------------ +// TOP SECRET TOP SECRET TOP SECRET TOP SECRET TOP SECRET +// ------------------------------------------------------------ diff --git a/patches/patches/076_sentinels.patch b/patches/patches/076_sentinels.patch new file mode 100644 index 0000000..52a5424 --- /dev/null +++ b/patches/patches/076_sentinels.patch @@ -0,0 +1,8 @@ +82c82 +< for (???) |s| { +--- +> for (my_seq) |s| { +94c94 +< while (??? != my_sentinel) { +--- +> while (my_seq[i] != my_sentinel) {