Adding exs 27-32

This commit is contained in:
Dave Gauer 2021-01-31 17:48:34 -05:00
parent 2de8a8c54d
commit c70fa5f58f
8 changed files with 271 additions and 5 deletions

25
27_defer.zig Normal file
View file

@ -0,0 +1,25 @@
//
// You can assign some code to run _after_ a block of code exits by
// deferring it with a "defer" statement:
//
// {
// defer runLater();
// runNow();
// }
//
// In the example above, runLater() will run when the block ({...})
// is finished. So the code above will run in the following order:
//
// runNow();
// runLater();
//
// This feature seems strange at first, but we'll see how it could be
// useful in the next exercise.
const std = @import("std");
pub fn main() void {
// Without changing anything else, please add a 'defer' statement
// to this code so that our program prints "One Two\n":
std.debug.print("Two\n", .{});
std.debug.print("One ", .{});
}

29
28_defer2.zig Normal file
View file

@ -0,0 +1,29 @@
//
// Now that you know how "defer" works, let's do something more
// interesting with it.
//
const std = @import("std");
pub fn main() void {
const animals = [_]u8{ 'g', 'c', 'd', 'd', 'g', 'z' };
for (animals) |a| printAnimal(a);
std.debug.print("done.\n", .{});
}
// This function is _supposed_ to print an animal name in parentheses
// like "(Goat) ", but we somehow need to print the end parenthesis
// even though this function can return in four different places!
fn printAnimal(animal: u8) void {
std.debug.print("(", .{});
std.debug.print(") ", .{}); // <---- how!?
if (animal == 'g'){ std.debug.print("Goat", .{}); return; }
if (animal == 'c'){ std.debug.print("Cat", .{}); return; }
if (animal == 'd'){ std.debug.print("Dog", .{}); return; }
std.debug.print("Unknown", .{});
}

60
29_errdefer.zig Normal file
View file

@ -0,0 +1,60 @@
//
// Another common problem is a block of code that could exit in multiple
// places due to an error - but that needs to run do something before it
// exits (typically to clean up after itself).
//
// An "errdefer" is a defer that only runs if the block exits with an error:
//
// {
// errdefer cleanup();
// try canFail();
// }
//
// The cleanup() function is called ONLY if the "try" statement returns an
// error produced by canFail().
//
const std = @import("std");
//
var counter: u32 = 0;
const MyErr = error{ GetFail, IncFail };
pub fn main() void {
// We simply quit the entire program if we fail to get a number:
var a: u32 = makeNumber() catch return;
var b: u32 = makeNumber() catch return;
std.debug.print("Numbers: {}, {}\n", .{a,b});
}
fn makeNumber() MyErr!u32 {
std.debug.print("Getting number...", .{});
// Please make the "failed" message print ONLY if the makeNumber()
// function exits with an error:
std.debug.print("failed!\n", .{});
var num = try getNumber(); // <-- This could fail!
num = try increaseNumber(num); // <-- This could ALSO fail!
std.debug.print("got {}. ", .{num});
return num;
}
fn getNumber() MyErr!u32 {
// I _could_ fail...but I don't!
return 4;
}
fn increaseNumber(n: u32) MyErr!u32 {
// I fail after the first time you run me!
if (counter > 0) return MyErr.IncFail;
// Sneaky, weird global stuff.
counter += 1;
return n + 1;
}

55
30_switch.zig Normal file
View file

@ -0,0 +1,55 @@
//
// The "switch" statement lets you match the possible values of an
// expression and perform a different action for each.
//
// This switch:
//
// switch (players) {
// 1 => startOnePlayerGame(),
// 2 => startTwoPlayerGame(),
// else => {
// alert();
// return GameError.TooManyPlayers;
// }
// }
//
// Is equivalent to this if/else:
//
// if (players == 1) startOnePlayerGame();
// else if (players == 2) startTwoPlayerGame();
// else {
// alert();
// return GameError.TooManyPlayers;
// }
//
//
//
const std = @import("std");
pub fn main() void {
const lang_chars = [_]u8{ 26, 9, 7, 42 };
for (lang_chars) |c| {
switch (c) {
1 => std.debug.print("A", .{}),
2 => std.debug.print("B", .{}),
3 => std.debug.print("C", .{}),
4 => std.debug.print("D", .{}),
5 => std.debug.print("E", .{}),
6 => std.debug.print("F", .{}),
7 => std.debug.print("G", .{}),
8 => std.debug.print("H", .{}),
9 => std.debug.print("I", .{}),
10 => std.debug.print("J", .{}),
// ... we don't need everything in between ...
25 => std.debug.print("Y", .{}),
26 => std.debug.print("Z", .{}),
// Switch statements must be "exhaustive" (there must be a
// match for every possible value). Please add an "else"
// to this switch to print a question mark "?" when c is
// not one of the existing matches.
}
}
std.debug.print("\n", .{});
}

42
31_switch2.zig Normal file
View file

@ -0,0 +1,42 @@
//
// What's really nice is that you can use a switch statement as an
// expression to return a value.
//
// var a = switch (x) {
// 1 => 9,
// 2 => 16,
// 3 => 7,
// ...
// }
//
const std = @import("std");
pub fn main() void {
const lang_chars = [_]u8{ 26, 9, 7, 42 };
for (lang_chars) |c| {
var real_char: u8 = switch (c) {
1 => 'A',
2 => 'B',
3 => 'C',
4 => 'D',
5 => 'E',
6 => 'F',
7 => 'G',
8 => 'H',
9 => 'I',
10 => 'J',
// ...
25 => 'Y',
26 => 'Z',
// As in the last exercise, please add the "else" clause
// and this time, have it return an exclamation mark "!".
};
std.debug.print("{c}", .{real_char});
// Note: "{c}" forces print() to display the value as a character.
// Can you guess what happens if you remove the "c"? Try it!
}
std.debug.print("\n", .{});
}

49
32_iferror.zig Normal file
View file

@ -0,0 +1,49 @@
//
// Let's revisit the very first error exercise. This time, we're going to
// look at a special error-handling type of the "if" statement.
//
// if (foo) |value| {
//
// // foo was NOT an error; value is the non-error value of foo
//
// } else |err| {
//
// // foo WAS an error; err is the error value of foo
//
// }
//
// We'll take it even further and use a switch statement to handle
// the error types.
//
const MyNumberError = error{
TooBig,
TooSmall,
};
const std = @import("std");
pub fn main() void {
var nums = [_]u8{2,3,4,5,6};
for (nums) |num| {
std.debug.print("{}", .{num});
var n = numberMaybeFail(num);
if (n) |value| {
std.debug.print("=4. ", .{});
} else |err| switch (err) {
MyNumberError.TooBig => std.debug.print(">4. ", .{}),
// Please add a match for TooSmall here and have it print: "<4. "
}
}
std.debug.print("\n", .{});
}
// This time we'll have numberMaybeFail() return an error union rather
// than a straight error.
fn numberMaybeFail(n: u8) MyNumberError!u8 {
if(n > 4) return MyNumberError.TooBig;
if(n < 4) return MyNumberError.TooSmall;
return n;
}

View file

@ -66,10 +66,9 @@ Planned exercises:
* [x] While * [x] While
* [x] For * [x] For
* [x] Functions * [x] Functions
* [ ] Errors * [x] Errors
* [ ] Defer * [x] Defer
* [ ] Switch * [x] Switch
* [ ] Runtime safety
* [ ] Unreachable * [ ] Unreachable
* [ ] Pointers * [ ] Pointers
* [ ] Pointer sized integers * [ ] Pointer sized integers

View file

@ -54,7 +54,7 @@ function check_it {
fi fi
# Wildcards to be lenient with anything AROUND the correct output # Wildcards to be lenient with anything AROUND the correct output
if [[ "$result" == *$correct_output* ]] if [[ "$result" == *"$correct_output"* ]]
then then
printf "${fmt_yay}** PASSED **${fmt_off}\n" printf "${fmt_yay}** PASSED **${fmt_off}\n"
else else
@ -94,6 +94,13 @@ check_it 23_errors3.zig "a=64, b=22"
check_it 24_errors4.zig "a=20, b=14, c=10" check_it 24_errors4.zig "a=20, b=14, c=10"
check_it 25_errors5.zig "a=0, b=19, c=0" check_it 25_errors5.zig "a=0, b=19, c=0"
check_it 26_hello2.zig "Hello world" "Try using a try!" check_it 26_hello2.zig "Hello world" "Try using a try!"
check_it 27_defer.zig "One Two"
check_it 28_defer2.zig "(Goat) (Cat) (Dog) (Dog) (Goat) (Unknown) done."
check_it 29_errdefer.zig "Getting number...got 5. Getting number...failed!"
check_it 30_switch.zig "ZIG?"
check_it 31_switch2.zig "ZIG!"
check_it 32_iferror.zig "2<4. 3<4. 4=4. 5>4. 6>4." "Seriously, what's the deal with fours?"
#check_it 33_quiz4.zig "foo" "Can you make this work?"
echo echo
echo " __ __ _ " echo " __ __ _ "