mirror of
https://codeberg.org/ziglings/exercises.git
synced 2024-12-26 01:20:30 +00:00
Compare commits
24 commits
97c2ea2f49
...
fb46871dab
Author | SHA1 | Date | |
---|---|---|---|
|
fb46871dab | ||
|
2606e53733 | ||
|
e55f8e8699 | ||
|
4d4eefa523 | ||
|
37a0f42621 | ||
|
731a3eb0a6 | ||
|
bfed660020 | ||
|
46e8fc0b61 | ||
|
8cce587d3b | ||
|
75e5e53497 | ||
|
c90d6015e3 | ||
|
150b3de299 | ||
|
fb018d212c | ||
|
d0d31ae73a | ||
|
530dcde3d4 | ||
|
286439cddc | ||
|
f629d78268 | ||
|
71b4f5ea81 | ||
|
8f49400aa1 | ||
|
e2b20b3406 | ||
|
02478759b4 | ||
|
173af62d34 | ||
|
9d1da38d38 | ||
|
775f5ebd29 |
11 changed files with 191 additions and 53 deletions
|
@ -1,31 +0,0 @@
|
||||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
||||||
// README at: https://github.com/devcontainers/templates/tree/main/src/debian
|
|
||||||
{
|
|
||||||
"name": "Ziglings",
|
|
||||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/base:bullseye",
|
|
||||||
"features": {
|
|
||||||
"ghcr.io/devcontainers-contrib/features/zig:1": {
|
|
||||||
"version": "master"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"customizations": {
|
|
||||||
"vscode": {
|
|
||||||
"extensions": [
|
|
||||||
"ziglang.vscode-zig"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
|
||||||
// "features": {},
|
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
||||||
// "forwardPorts": [],
|
|
||||||
|
|
||||||
// Configure tool-specific properties.
|
|
||||||
// "customizations": {},
|
|
||||||
|
|
||||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
||||||
// "remoteUser": "root"
|
|
||||||
}
|
|
|
@ -1201,6 +1201,13 @@ const exercises = [_]Exercise{
|
||||||
.main_file = "108_labeled_switch.zig",
|
.main_file = "108_labeled_switch.zig",
|
||||||
.output = "The pull request has been merged.",
|
.output = "The pull request has been merged.",
|
||||||
},
|
},
|
||||||
|
.{
|
||||||
|
.main_file = "109_vectors.zig",
|
||||||
|
.output =
|
||||||
|
\\Max difference (old fn): 0.014
|
||||||
|
\\Max difference (new fn): 0.014
|
||||||
|
,
|
||||||
|
},
|
||||||
.{
|
.{
|
||||||
.main_file = "999_the_end.zig",
|
.main_file = "999_the_end.zig",
|
||||||
.output =
|
.output =
|
||||||
|
|
|
@ -190,7 +190,7 @@ const TripItem = union(enum) {
|
||||||
fn printMe(self: TripItem) void {
|
fn printMe(self: TripItem) void {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
// Oops! The hermit forgot how to capture the union values
|
// Oops! The hermit forgot how to capture the union values
|
||||||
// in a switch statement. Please capture both values as
|
// in a switch statement. Please capture each value as
|
||||||
// 'p' so the print statements work!
|
// 'p' so the print statements work!
|
||||||
.place => print("{s}", .{p.name}),
|
.place => print("{s}", .{p.name}),
|
||||||
.path => print("--{}->", .{p.dist}),
|
.path => print("--{}->", .{p.dist}),
|
||||||
|
|
|
@ -110,15 +110,15 @@ pub fn main() void {
|
||||||
// name will not be printed if the field is of type 'void'
|
// name will not be printed if the field is of type 'void'
|
||||||
// (which is a zero-bit type that takes up no space at all!):
|
// (which is a zero-bit type that takes up no space at all!):
|
||||||
if (fields[0].??? != void) {
|
if (fields[0].??? != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[0].name});
|
print(" {s}", .{fields[0].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields[1].??? != void) {
|
if (fields[1].??? != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[1].name});
|
print(" {s}", .{fields[1].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields[2].??? != void) {
|
if (fields[2].??? != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[2].name});
|
print(" {s}", .{fields[2].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yuck, look at all that repeated code above! I don't know
|
// Yuck, look at all that repeated code above! I don't know
|
||||||
|
@ -136,14 +136,16 @@ pub fn main() void {
|
||||||
// But a change after Zig 0.10.0 added the source file name to the
|
// But a change after Zig 0.10.0 added the source file name to the
|
||||||
// type. "Narcissus" became "065_builtins2.Narcissus".
|
// type. "Narcissus" became "065_builtins2.Narcissus".
|
||||||
//
|
//
|
||||||
// To fix this, I've added this function to strip the filename from
|
// To fix this, we've added this function to strip the filename from
|
||||||
// the front of the type name in the dumbest way possible. (It returns
|
// the front of the type name. (It returns a slice of the type name
|
||||||
// a slice of the type name starting at character 14 (assuming
|
// starting at the index + 1 of character ".")
|
||||||
// single-byte characters).
|
|
||||||
//
|
//
|
||||||
// We'll be seeing @typeName again in Exercise 070. For now, you can
|
// We'll be seeing @typeName again in Exercise 070. For now, you can
|
||||||
// see that it takes a Type and returns a u8 "string".
|
// see that it takes a Type and returns a u8 "string".
|
||||||
fn maximumNarcissism(myType: anytype) []const u8 {
|
fn maximumNarcissism(myType: anytype) []const u8 {
|
||||||
// Turn '065_builtins2.Narcissus' into 'Narcissus'
|
const indexOf = @import("std").mem.indexOf;
|
||||||
return @typeName(myType)[14..];
|
|
||||||
|
// Turn "065_builtins2.Narcissus" into "Narcissus"
|
||||||
|
const name = @typeName(myType);
|
||||||
|
return name[indexOf(u8, name, ".").? + 1 ..];
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
// Therefore, the comments for the format() function are the only
|
// Therefore, the comments for the format() function are the only
|
||||||
// way to definitively learn how to format strings in Zig:
|
// way to definitively learn how to format strings in Zig:
|
||||||
//
|
//
|
||||||
// https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L29
|
// https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L33
|
||||||
//
|
//
|
||||||
// Zig already has a very nice selection of formatting options.
|
// Zig already has a very nice selection of formatting options.
|
||||||
// These can be used in different ways, but generally to convert
|
// These can be used in different ways, but generally to convert
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
// in practice. Because either you don't need the precision, or you use a
|
// in practice. Because either you don't need the precision, or you use a
|
||||||
// calculator in which the number is stored as a very precise constant.
|
// calculator in which the number is stored as a very precise constant.
|
||||||
// But at some point this constant was calculated and we are doing the same
|
// But at some point this constant was calculated and we are doing the same
|
||||||
// now.The question at this point is, how many partial values do we have
|
// now. The question at this point is, how many partial values do we have
|
||||||
// to calculate for which accuracy?
|
// to calculate for which accuracy?
|
||||||
//
|
//
|
||||||
// The answer is chewing, to get 8 digits after the decimal point we need
|
// The answer is chewing, to get 8 digits after the decimal point we need
|
||||||
|
|
147
exercises/109_vectors.zig
Normal file
147
exercises/109_vectors.zig
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// So far in Ziglings, we've seen how for loops can be used to
|
||||||
|
// repeat calculations across an array in several ways.
|
||||||
|
//
|
||||||
|
// For loops are generally great for this kind of task, but
|
||||||
|
// sometimes they don't fully utilize the capabilities of the
|
||||||
|
// CPU.
|
||||||
|
//
|
||||||
|
// Most modern CPUs can execute instructions in which SEVERAL
|
||||||
|
// calculations are performed WITHIN registers at the SAME TIME.
|
||||||
|
// These are known as "single instruction, multiple data" (SIMD)
|
||||||
|
// instructions. SIMD instructions can make code significantly
|
||||||
|
// more performant.
|
||||||
|
//
|
||||||
|
// To see why, imagine we have a program in which we take the
|
||||||
|
// square root of four (changing) f32 floats.
|
||||||
|
//
|
||||||
|
// A simple compiler would take the program and produce machine code
|
||||||
|
// which calculates each square root sequentially. Most registers on
|
||||||
|
// modern CPUs have 64 bits, so we could imagine that each float moves
|
||||||
|
// into a 64-bit register, and the following happens four times:
|
||||||
|
//
|
||||||
|
// 32 bits 32 bits
|
||||||
|
// +-------------------+
|
||||||
|
// register | 0 | x |
|
||||||
|
// +-------------------+
|
||||||
|
//
|
||||||
|
// |
|
||||||
|
// [SQRT instruction]
|
||||||
|
// V
|
||||||
|
//
|
||||||
|
// +-------------------+
|
||||||
|
// | 0 | sqrt(x) |
|
||||||
|
// +-------------------+
|
||||||
|
//
|
||||||
|
// Notice that half of the register contains blank data to which
|
||||||
|
// nothing happened. What a waste! What if we were able to use
|
||||||
|
// that space instead? This is the idea at the core of SIMD.
|
||||||
|
//
|
||||||
|
// Most modern CPUs contain specialized registers with at least 128 bits
|
||||||
|
// for performing SIMD instructions. On a machine with 128-bit SIMD
|
||||||
|
// registers, a smart compiler would probably NOT issue four sqrt
|
||||||
|
// instructions as above, but instead pack the floats into a single
|
||||||
|
// 128-bit register, then execute a single "packed" sqrt
|
||||||
|
// instruction to do ALL the square root calculations at once.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// 32 bits 32 bits 32 bits 32 bits
|
||||||
|
// +---------------------------------------+
|
||||||
|
// register | 4.0 | 9.0 | 25.0 | 49.0 |
|
||||||
|
// +---------------------------------------+
|
||||||
|
//
|
||||||
|
// |
|
||||||
|
// [SIMD SQRT instruction]
|
||||||
|
// V
|
||||||
|
//
|
||||||
|
// +---------------------------------------+
|
||||||
|
// register | 2.0 | 3.0 | 5.0 | 7.0 |
|
||||||
|
// +---------------------------------------+
|
||||||
|
//
|
||||||
|
// Pretty cool, right?
|
||||||
|
//
|
||||||
|
// Code with SIMD instructions is usually more performant than code
|
||||||
|
// without SIMD instructions. Zig cares a lot about performance,
|
||||||
|
// so it has built-in support for SIMD! It has a data structure that
|
||||||
|
// directly supports SIMD instructions:
|
||||||
|
//
|
||||||
|
// +-----------+
|
||||||
|
// | Vectors |
|
||||||
|
// +-----------+
|
||||||
|
//
|
||||||
|
// Operations performed on vectors in Zig will be done in parallel using
|
||||||
|
// SIMD instructions, whenever possible.
|
||||||
|
//
|
||||||
|
// Defining vectors in Zig is straightforwards. No library import is needed.
|
||||||
|
const v1 = @Vector(3, i32){ 1, 10, 100 };
|
||||||
|
const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };
|
||||||
|
|
||||||
|
// Vectors support the same builtin operators as their underlying base types.
|
||||||
|
const v3 = v1 + v1; // { 2, 20, 200};
|
||||||
|
const v4 = v2 * v2; // { 4.0, 9.0, 25.0};
|
||||||
|
|
||||||
|
// Intrinsics that apply to base types usually extend to vectors.
|
||||||
|
const v5: @Vector(3, f32) = @floatFromInt(v3); // { 2.0, 20.0, 200.0}
|
||||||
|
const v6 = v4 - v5; // { 2.0, -11.0, -175.0}
|
||||||
|
const v7 = @abs(v6); // { 2.0, 11.0, 175.0}
|
||||||
|
|
||||||
|
// We can make constant vectors, and reduce vectors.
|
||||||
|
const v8: @Vector(4, u8) = @splat(2); // { 2, 2, 2, 2}
|
||||||
|
const v8_sum = @reduce(.Add, v8); // 8
|
||||||
|
const v8_min = @reduce(.Min, v8); // 2
|
||||||
|
|
||||||
|
// Fixed-length arrays can be automatically assigned to vectors (and vice-versa).
|
||||||
|
const single_digit_primes = [4]i8{ 2, 3, 5, 7 };
|
||||||
|
const prime_vector: @Vector(4, i8) = single_digit_primes;
|
||||||
|
|
||||||
|
// Now let's use vectors to simplify and optimize some code!
|
||||||
|
//
|
||||||
|
// Ewa is writing a program in which they frequently want to compare
|
||||||
|
// two lists of four f32s. Ewa expects the lists to be similar, and
|
||||||
|
// wants to determine the largest pairwise difference between the lists.
|
||||||
|
//
|
||||||
|
// Ewa wrote the following function to figure this out.
|
||||||
|
|
||||||
|
fn calcMaxPairwiseDiffOld(list1: [4]f32, list2: [4]f32) f32 {
|
||||||
|
var max_diff: f32 = 0;
|
||||||
|
for (list1, list2) |n1, n2| {
|
||||||
|
const abs_diff = @abs(n1 - n2);
|
||||||
|
if (abs_diff > max_diff) {
|
||||||
|
max_diff = abs_diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ewa heard about vectors in Zig, and started writing a new vector
|
||||||
|
// version of the function, but has got stuck!
|
||||||
|
//
|
||||||
|
// Help Ewa finish the vector version! The examples above should help.
|
||||||
|
|
||||||
|
const Vec4 = @Vector(4, f32);
|
||||||
|
fn calcMaxPairwiseDiffNew(a: Vec4, b: Vec4) f32 {
|
||||||
|
const abs_diff_vec = ???;
|
||||||
|
const max_diff = @reduce(???, abs_diff_vec);
|
||||||
|
return max_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quite the simplification! We could even write the function in one line
|
||||||
|
// and it would still be readable.
|
||||||
|
//
|
||||||
|
// Since the entire function is now expressed in terms of vector operations,
|
||||||
|
// the Zig compiler will easily be able to compile it down to machine code
|
||||||
|
// which utilizes the all-powerful SIMD instructions and does a lot of the
|
||||||
|
// computation in parallel.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const print = std.debug.print;
|
||||||
|
|
||||||
|
pub fn main() void {
|
||||||
|
const l1 = [4]f32{ 3.141, 2.718, 0.577, 1.000 };
|
||||||
|
const l2 = [4]f32{ 3.154, 2.707, 0.591, 0.993 };
|
||||||
|
const mpd_old = calcMaxPairwiseDiffOld(l1, l2);
|
||||||
|
const mpd_new = calcMaxPairwiseDiffNew(l1, l2);
|
||||||
|
print("Max difference (old fn): {d: >5.3}\n", .{mpd_old});
|
||||||
|
print("Max difference (new fn): {d: >5.3}\n", .{mpd_new});
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
--- exercises/058_quiz7.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/058_quiz7.zig 2024-10-28 09:06:49.448505460 +0100
|
||||||
+++ answers/058_quiz7.zig 2023-10-05 20:04:07.106101152 +0200
|
+++ answers/058_quiz7.zig 2024-10-28 09:35:14.631932322 +0100
|
||||||
@@ -192,8 +192,8 @@
|
@@ -192,8 +192,8 @@
|
||||||
// Oops! The hermit forgot how to capture the union values
|
// Oops! The hermit forgot how to capture the union values
|
||||||
// in a switch statement. Please capture both values as
|
// in a switch statement. Please capture each value as
|
||||||
// 'p' so the print statements work!
|
// 'p' so the print statements work!
|
||||||
- .place => print("{s}", .{p.name}),
|
- .place => print("{s}", .{p.name}),
|
||||||
- .path => print("--{}->", .{p.dist}),
|
- .path => print("--{}->", .{p.dist}),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--- exercises/065_builtins2.zig 2024-09-02 19:15:56.569952315 +0200
|
--- exercises/065_builtins2.zig 2024-11-02 16:58:30.607829441 +0100
|
||||||
+++ answers/065_builtins2.zig 2024-09-02 19:13:44.280600350 +0200
|
+++ answers/065_builtins2.zig 2024-11-02 16:58:33.821220588 +0100
|
||||||
@@ -58,7 +58,7 @@
|
@@ -58,7 +58,7 @@
|
||||||
// Oops! We cannot leave the 'me' and 'myself' fields
|
// Oops! We cannot leave the 'me' and 'myself' fields
|
||||||
// undefined. Please set them here:
|
// undefined. Please set them here:
|
||||||
|
@ -24,16 +24,16 @@
|
||||||
// (which is a zero-bit type that takes up no space at all!):
|
// (which is a zero-bit type that takes up no space at all!):
|
||||||
- if (fields[0].??? != void) {
|
- if (fields[0].??? != void) {
|
||||||
+ if (fields[0].type != void) {
|
+ if (fields[0].type != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[0].name});
|
print(" {s}", .{fields[0].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
- if (fields[1].??? != void) {
|
- if (fields[1].??? != void) {
|
||||||
+ if (fields[1].type != void) {
|
+ if (fields[1].type != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[1].name});
|
print(" {s}", .{fields[1].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
- if (fields[2].??? != void) {
|
- if (fields[2].??? != void) {
|
||||||
+ if (fields[2].type != void) {
|
+ if (fields[2].type != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[2].name});
|
print(" {s}", .{fields[2].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--- exercises/099_formatting.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/099_formatting.zig 2024-11-07 21:45:10.459123650 +0100
|
||||||
+++ answers/099_formatting.zig 2023-10-05 20:04:07.292771311 +0200
|
+++ answers/099_formatting.zig 2024-11-07 21:43:55.154345991 +0100
|
||||||
@@ -131,7 +131,7 @@
|
@@ -131,7 +131,7 @@
|
||||||
for (0..size) |b| {
|
for (0..size) |b| {
|
||||||
// What formatting is needed here to make our columns
|
// What formatting is needed here to make our columns
|
||||||
|
|
13
patches/patches/109_vectors.patch
Normal file
13
patches/patches/109_vectors.patch
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
--- exercises/109_vectors.zig 2024-11-07 14:57:09.673383618 +0100
|
||||||
|
+++ answers/109_vectors.zig 2024-11-07 14:22:59.069150138 +0100
|
||||||
|
@@ -121,8 +121,8 @@
|
||||||
|
|
||||||
|
const Vec4 = @Vector(4, f32);
|
||||||
|
fn calcMaxPairwiseDiffNew(a: Vec4, b: Vec4) f32 {
|
||||||
|
- const abs_diff_vec = ???;
|
||||||
|
- const max_diff = @reduce(???, abs_diff_vec);
|
||||||
|
+ const abs_diff_vec = @abs(a - b);
|
||||||
|
+ const max_diff = @reduce(.Max, abs_diff_vec);
|
||||||
|
return max_diff;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue