mirror of
https://codeberg.org/ziglings/exercises.git
synced 2024-12-25 09:10:26 +00:00
Adding exs 27-32
This commit is contained in:
parent
2de8a8c54d
commit
c70fa5f58f
8 changed files with 271 additions and 5 deletions
25
27_defer.zig
Normal file
25
27_defer.zig
Normal 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
29
28_defer2.zig
Normal 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
60
29_errdefer.zig
Normal 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
55
30_switch.zig
Normal 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
42
31_switch2.zig
Normal 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
49
32_iferror.zig
Normal 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;
|
||||
}
|
|
@ -66,10 +66,9 @@ Planned exercises:
|
|||
* [x] While
|
||||
* [x] For
|
||||
* [x] Functions
|
||||
* [ ] Errors
|
||||
* [ ] Defer
|
||||
* [ ] Switch
|
||||
* [ ] Runtime safety
|
||||
* [x] Errors
|
||||
* [x] Defer
|
||||
* [x] Switch
|
||||
* [ ] Unreachable
|
||||
* [ ] Pointers
|
||||
* [ ] Pointer sized integers
|
||||
|
|
9
ziglings
9
ziglings
|
@ -54,7 +54,7 @@ function check_it {
|
|||
fi
|
||||
|
||||
# Wildcards to be lenient with anything AROUND the correct output
|
||||
if [[ "$result" == *$correct_output* ]]
|
||||
if [[ "$result" == *"$correct_output"* ]]
|
||||
then
|
||||
printf "${fmt_yay}** PASSED **${fmt_off}\n"
|
||||
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 25_errors5.zig "a=0, b=19, c=0"
|
||||
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 " __ __ _ "
|
||||
|
|
Loading…
Reference in a new issue