mirror of
https://codeberg.org/ziglings/exercises.git
synced 2026-06-08 07:50:00 +00:00
Merge pull request 'Replace exercise 074_comptime9' (#391) from tjk/ziglings-exercises:comptime9 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/391
This commit is contained in:
@@ -1075,7 +1075,7 @@ const exercises = [_]Exercise{
|
||||
},
|
||||
.{
|
||||
.main_file = "074_comptime9.zig",
|
||||
.output = "My llama value is 2.",
|
||||
.output = "MouseLlama joins the crew!",
|
||||
},
|
||||
.{
|
||||
.main_file = "075_quiz8.zig",
|
||||
|
||||
@@ -1,62 +1,111 @@
|
||||
//
|
||||
// In addition to knowing when to use the 'comptime' keyword,
|
||||
// it's also good to know when you DON'T need it.
|
||||
//
|
||||
// The following contexts are already IMPLICITLY evaluated at
|
||||
// compile time, and adding the 'comptime' keyword would be
|
||||
// superfluous, redundant, and smelly:
|
||||
//
|
||||
// * The container-level scope (outside of any function in a source file)
|
||||
// * Type declarations of:
|
||||
// * Variables
|
||||
// * Functions (types of parameters and return values)
|
||||
// * Structs
|
||||
// * Unions
|
||||
// * Enums
|
||||
// * The test expressions in inline for and while loops
|
||||
// * An expression passed to the @cImport() builtin
|
||||
//
|
||||
// Work with Zig for a while, and you'll start to develop an
|
||||
// intuition for these contexts. Let's work on that now.
|
||||
//
|
||||
// You have been given just one 'comptime' statement to use in
|
||||
// the program below. Here it is:
|
||||
//
|
||||
// comptime
|
||||
//
|
||||
// Just one is all it takes. Use it wisely!
|
||||
//
|
||||
const print = @import("std").debug.print;
|
||||
|
||||
// Being in the container-level scope, everything about this value is
|
||||
// implicitly required to be known compile time.
|
||||
const llama_count = 5;
|
||||
// We're going to (ab)use the power of Zig to make animal hybrid creatures!
|
||||
// What do you think a GatorMouse would look like? Eek.
|
||||
//
|
||||
// Let's try a MouseLlama instead.
|
||||
//
|
||||
// We'll make a function that runs at comptime and takes a short code describing
|
||||
// the desired creature. A Mouse is represented by "m" and a Llama is "lm".
|
||||
// A MouseLlama hybrid, then, would be represented by "mlm".
|
||||
|
||||
// Again, this value's type and size must be known at compile
|
||||
// time, but we're letting the compiler infer both from the
|
||||
// return type of a function.
|
||||
const llamas = makeLlamas(llama_count);
|
||||
const Animal = enum {
|
||||
Mouse,
|
||||
Llama,
|
||||
Gator,
|
||||
};
|
||||
|
||||
// And here's the function. Note that the return value type
|
||||
// depends on one of the input arguments!
|
||||
fn makeLlamas(count: usize) [count]u8 {
|
||||
var temp: [count]u8 = undefined;
|
||||
var i = 0;
|
||||
// makeCreature takes the count of animals making up the hybrid creature (so we
|
||||
// know how big a pen we'll need) and a format string, like the "mlm" for
|
||||
// MouseLlama.
|
||||
fn makeCreature(comptime count: usize, comptime fmt: []const u8) [count]Animal {
|
||||
|
||||
// Note that this does NOT need to be an inline 'while'.
|
||||
while (i < count) : (i += 1) {
|
||||
temp[i] = i;
|
||||
// Since not every animal is represented by a single character, we need to
|
||||
// track the state of things as we move along. For example, if we see an
|
||||
// "m", is that a new Mouse or the end of a Llama?
|
||||
const State = enum {
|
||||
start, // Ready to start a new animal.
|
||||
l, // This means we've seen an "l", so if we see an "m", we know it's a Llama.
|
||||
};
|
||||
var state = State.start;
|
||||
|
||||
// We return an array of animals representing the creature. (This is why we
|
||||
// really needed the 'count' parameter. Arrays need a size.)
|
||||
var animals: [count]Animal = .{undefined} ** count;
|
||||
var next_animal: usize = 0;
|
||||
|
||||
inline for (fmt) |char| {
|
||||
|
||||
// This is a good spot to add a @compileLog() call if you need to debug
|
||||
// any variables... (Come back here after you see main().)
|
||||
|
||||
switch (state) {
|
||||
.start => switch (char) {
|
||||
// We've seen the start of a Llama.
|
||||
'l' => state = .l,
|
||||
|
||||
// Mice are smaller. An "m" is a full Mouse.
|
||||
'm' => {
|
||||
animals[next_animal] = .Mouse;
|
||||
next_animal += 1;
|
||||
},
|
||||
|
||||
// @compileError lets us stop the build immediately if something
|
||||
// is wrong. It's like @compileLog but it prints a message
|
||||
// instead of inspecting values.
|
||||
//
|
||||
// What do you think happens with Gators? Do they join with
|
||||
// other animals or is this an error?
|
||||
'g' => ???,
|
||||
|
||||
else => @compileError("No animal starts with '" ++ char ++ "'!"),
|
||||
},
|
||||
|
||||
.l => switch (char) {
|
||||
// We've seen the end of a Llama.
|
||||
'm' => {
|
||||
animals[next_animal] = .Llama;
|
||||
next_animal += 1;
|
||||
// Something is missing here. After we finish a Llama, we
|
||||
// need to be ready to _start_ over with a new animal...
|
||||
???
|
||||
},
|
||||
|
||||
else => @compileError("Only llamas start with 'l'!"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
if (state != .start) {
|
||||
@compileError("Oh no, an incomplete llama!");
|
||||
}
|
||||
if (next_animal != count) {
|
||||
@compileError("Creature is missing an animal (format string too short).");
|
||||
}
|
||||
|
||||
return animals;
|
||||
}
|
||||
|
||||
pub fn main() void {
|
||||
print("My llama value is {}.\n", .{llamas[2]});
|
||||
// Once you've fixed the ??? marks above, this makeCreature call will still
|
||||
// only succeed if you move it outside of main, so it will run at comptime.
|
||||
//
|
||||
// With the call here, Zig will try to make the creature at runtime, and
|
||||
// you'll get an interesting error.
|
||||
//
|
||||
// You may think the state got mixed up, but if you use @compileLog to check
|
||||
// some variables in makeCreature, you'll see that Zig is trying to compare
|
||||
// comptime values with "[runtime value]", which will never match.
|
||||
//
|
||||
// You can solve this by adding "comptime" to two of the variables in
|
||||
// makeCreature...
|
||||
const creature = makeCreature(2, "mlm");
|
||||
|
||||
for (creature) |animal| {
|
||||
// @tagName gives us a string representing which variant of an enum we
|
||||
// have. This lets us print the names of animals without repeating them
|
||||
// here.
|
||||
print("{s}", .{@tagName(animal)});
|
||||
}
|
||||
print(" joins the crew!", .{});
|
||||
}
|
||||
//
|
||||
// The lesson here is to not pepper your program with 'comptime'
|
||||
// keywords unless you need them. Between the implicit compile
|
||||
// time contexts and Zig's aggressive evaluation of any
|
||||
// expression it can figure out at compile time, it's sometimes
|
||||
// surprising how few places actually need the keyword.
|
||||
|
||||
@@ -1,11 +1,35 @@
|
||||
--- exercises/074_comptime9.zig 2023-10-03 22:15:22.125574535 +0200
|
||||
+++ answers/074_comptime9.zig 2023-10-05 20:04:07.176102462 +0200
|
||||
@@ -39,7 +39,7 @@
|
||||
--- exercises/074_comptime9.zig 2026-04-11 21:35:08.132459373 -0700
|
||||
+++ answers/074_comptime9.zig 2026-04-12 07:13:37.971010827 -0700
|
||||
@@ -27,12 +27,12 @@
|
||||
start, // Ready to start a new animal.
|
||||
l, // This means we've seen an "l", so if we see an "m", we know it's a Llama.
|
||||
};
|
||||
- var state = State.start;
|
||||
+ comptime var state = State.start;
|
||||
|
||||
// And here's the function. Note that the return value type
|
||||
// depends on one of the input arguments!
|
||||
-fn makeLlamas(count: usize) [count]u8 {
|
||||
+fn makeLlamas(comptime count: usize) [count]u8 {
|
||||
var temp: [count]u8 = undefined;
|
||||
var i = 0;
|
||||
// We return an array of animals representing the creature. (This is why we
|
||||
// really needed the 'count' parameter. Arrays need a size.)
|
||||
var animals: [count]Animal = .{undefined} ** count;
|
||||
- var next_animal: usize = 0;
|
||||
+ comptime var next_animal: usize = 0;
|
||||
|
||||
inline for (fmt) |char| {
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
//
|
||||
// What do you think happens with Gators? Do they join with
|
||||
// other animals or is this an error?
|
||||
- 'g' => ???,
|
||||
+ 'g' => @compileError("Gators refuse to join with other animals."),
|
||||
|
||||
else => @compileError("No animal starts with '" ++ char ++ "'!"),
|
||||
},
|
||||
@@ -68,7 +68,7 @@
|
||||
next_animal += 1;
|
||||
// Something is missing here. After we finish a Llama, we
|
||||
// need to be ready to _start_ over with a new animal...
|
||||
- ???
|
||||
+ state = .start;
|
||||
},
|
||||
|
||||
else => @compileError("Only llamas start with 'l'!"),
|
||||
|
||||
Reference in New Issue
Block a user