mirror of
https://codeberg.org/andyscott/ziglings.git
synced 2024-11-09 11:40:46 -05:00
Consistent instructions and examples
I started off with "hints" that required the poor student to piece together the information from incomplete bits. A complete example is like a picture that is worth 1000 words and far clearer.
This commit is contained in:
parent
507355ec3b
commit
adf5ddb27d
16 changed files with 185 additions and 89 deletions
14
01_hello.zig
14
01_hello.zig
|
@ -2,11 +2,17 @@
|
|||
// Oh no! This program is supposed to print "Hello world!" but it needs
|
||||
// your help!
|
||||
//
|
||||
// Hint: Zig functions are private by default.
|
||||
// The main() function should be public.
|
||||
// Declare a public function with "pub fn ..."
|
||||
//
|
||||
// Try to fix the program and run `ziglings` to see if it passes.
|
||||
// Zig functions are private by default but the main() function should
|
||||
// be public.
|
||||
//
|
||||
// A function is declared public with the "pub" statement like so:
|
||||
//
|
||||
// pub fn foo() void {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// Try to fix the program and run `ziglings` to see if it works!
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
|
|
15
02_std.zig
15
02_std.zig
|
@ -2,13 +2,16 @@
|
|||
// Oops! This program is supposed to print a line like our Hello World
|
||||
// example. But we forgot how to import the Zig Standard Library.
|
||||
//
|
||||
// Hint 1: The @import() built-in function returns a value representing
|
||||
// imported code. We need to give that value a name to use it.
|
||||
// Hint 2: We use the name "std" in the main function (see below).
|
||||
// Hint 3: Imports need to be named by declaring them as "const" values.
|
||||
// Hint 4: Take a look at how the previous exercise did this!
|
||||
// The @import() function is built into Zig. It returns a value which
|
||||
// represents the imported code. It's a good idea to store the import as
|
||||
// a constant value with the same name as the import:
|
||||
//
|
||||
@import("std");
|
||||
// const foo = @import("foo");
|
||||
//
|
||||
// Please complete the import below:
|
||||
//
|
||||
|
||||
??? = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
std.debug.print("Standard Library.\n", .{});
|
||||
|
|
|
@ -1,14 +1,32 @@
|
|||
//
|
||||
// Oh dear! It seems we got a little carried away making const u8 values.
|
||||
// * const means constant (cannot be changed)
|
||||
// * u8 means unsigned (cannot be negative), 8-bit integer
|
||||
// It seems we got a little carried away making everything "const u8"!
|
||||
//
|
||||
// Hint 1: Use 'var' for values that can change.
|
||||
// Hint 2: Use enough bits to hold the value you want:
|
||||
// u8 255
|
||||
// u16 65,535
|
||||
// u32 4,294,967,295
|
||||
// Hint 3: Use 'i' (e.g. 'i8', 'i16') for signed integers.
|
||||
// "const" values cannot change.
|
||||
// "u" types are "unsigned" and cannot store negative values.
|
||||
// "8" means the type is 8 bits in size.
|
||||
//
|
||||
// Example: foo cannot change (it is CONSTant)
|
||||
// bar can change (it is VARiable):
|
||||
//
|
||||
// const foo: u8 = 20;
|
||||
// var bar: u8 = 20;
|
||||
//
|
||||
// Example: foo cannot be negative and can hold 0 to 255
|
||||
// bar CAN be negative and can hold −128 to 127
|
||||
//
|
||||
// const foo: u8 = 20;
|
||||
// var bar: i8 = -20;
|
||||
//
|
||||
// Example: foo can hold 8 bits (0 to 255)
|
||||
// bar can hold 16 bits (0 to 65,535)
|
||||
//
|
||||
// You can do just about any combination of these that you can think of:
|
||||
//
|
||||
// u32 can hold 0 to 4,294,967,295
|
||||
// i64 can hold −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
|
||||
//
|
||||
// Please fix this program so that the types can hold the desired values
|
||||
// and the errors go away!
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
|
|
|
@ -1,31 +1,49 @@
|
|||
//
|
||||
// Let's learn some array basics. Arrays are declared with:
|
||||
//
|
||||
// const foo [size]<type> = [size]<type>{ values };
|
||||
// var foo [3]u32 = [3]u32{ 42, 108, 5423 };
|
||||
//
|
||||
// When Zig can infer the size of the array, you can use '_' for the
|
||||
// size. You can also let Zig infer the type of the value so the
|
||||
// declaration is much less verbose.
|
||||
//
|
||||
// const foo = [_]<type>{ values };
|
||||
// var foo = [_]u32{ 42, 108, 5423 };
|
||||
//
|
||||
// Get values of an array using array[index] notation:
|
||||
//
|
||||
// const bar = foo[3]; // 5423
|
||||
//
|
||||
// Set values of an array using array[index] notation:
|
||||
//
|
||||
// foo[3] = 16;
|
||||
//
|
||||
// Get the length of an array using the len property:
|
||||
//
|
||||
// const length = foo.len;
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
|
||||
// (Problem 1)
|
||||
// This "const" is going to cause a problem later - can you see what it is?
|
||||
// How do we fix it?
|
||||
const some_primes = [_]u8{ 1, 3, 5, 7, 11, 13, 17, 19 };
|
||||
|
||||
// Individual values can be set with '[]' notation. Let's fix
|
||||
// the first prime (it should be 2!):
|
||||
// Individual values can be set with '[]' notation.
|
||||
// Example: This line changes the first prime to 2 (which is correct):
|
||||
some_primes[0] = 2;
|
||||
|
||||
// Individual values can also be accessed with '[]' notation.
|
||||
// Example: This line stores the first prime in "first":
|
||||
const first = some_primes[0];
|
||||
|
||||
// Looks like we need to complete this expression (like 'first'):
|
||||
const fourth = ???;
|
||||
// (Problem 2)
|
||||
// Looks like we need to complete this expression. Use the example
|
||||
// above to set "fourth" to the fourth element of the some_primes array:
|
||||
const fourth = some_primes[???];
|
||||
|
||||
// Use '.len' to get the length of the array:
|
||||
// (Problem 3)
|
||||
// Use the len property to get the length of the array:
|
||||
const length = some_primes.???;
|
||||
|
||||
std.debug.print("First: {}, Fourth: {}, Length: {}\n",
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
// Zig has some fun array operators.
|
||||
//
|
||||
// You can use '++' to concatenate two arrays:
|
||||
//
|
||||
// const a = [_]u8{ 1,2 };
|
||||
// const b = [_]u8{ 3,4 };
|
||||
// const c = a ++ b ++ [_]u8{ 5 }; // 1,2,3,4,5
|
||||
// const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5
|
||||
//
|
||||
// You can use '**' to repeat an array:
|
||||
// const d = [_]u8{ 1,2,3 } ** 2; // 1,2,3,1,2,3
|
||||
//
|
||||
// const d = [_]u8{ 1,2,3 } ** 2; // equals 1 2 3 1 2 3
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
|
@ -15,15 +17,20 @@ pub fn main() void {
|
|||
const le = [_]u8{ 1, 3 };
|
||||
const et = [_]u8{ 3, 7 };
|
||||
|
||||
// I want this to contain digits: 1 3 3 7
|
||||
// (Problem 1)
|
||||
// Please set this array concatenating the two arrays above.
|
||||
// It should result in: 1 3 3 7
|
||||
const leet = ???;
|
||||
|
||||
// I want this to contain digits: 1 0 0 1 1 0 0 1 1 0 0 1
|
||||
// (Problem 2)
|
||||
// Please set this array to using repetition.
|
||||
// It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
|
||||
const bit_pattern = [_]u8{ ??? } ** 3;
|
||||
|
||||
|
||||
// Okay, that's all of the problems. Let's see the results.
|
||||
//
|
||||
// We could print these arrays with leet[0], leet[1],...but let's
|
||||
// have a little preview of Zig 'for' loops instead!
|
||||
// have a little preview of Zig "for" loops instead:
|
||||
std.debug.print("LEET: ", .{});
|
||||
|
||||
for (leet) |*n| {
|
||||
|
|
|
@ -3,38 +3,46 @@
|
|||
//
|
||||
// We've already seen Zig string literals: "Hello world.\n"
|
||||
//
|
||||
// Like the C language, Zig stores strings as arrays of bytes
|
||||
// encoded as UTF-8 characters terminated with a null value.
|
||||
// For now, just focus on the fact that strings are arrays of
|
||||
// characters!
|
||||
// Zig stores strings as arrays of bytes.
|
||||
//
|
||||
// const foo = "Hello";
|
||||
//
|
||||
// Is the same as:
|
||||
//
|
||||
// const foo = [_]u8{ 'H', 'e', 'l', 'l', 'o' };
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
const ziggy = "stardust";
|
||||
|
||||
// (Problem 1)
|
||||
// Use array square bracket syntax to get the letter 'd' from
|
||||
// the string "stardust" above.
|
||||
const d: u8 = ziggy[???];
|
||||
|
||||
// (Problem 2)
|
||||
// Use the array repeat '**' operator to make "ha ha ha".
|
||||
const laugh = "ha " ???;
|
||||
|
||||
// (Problem 3)
|
||||
// Use the array concatenation '++' operator to make "Major Tom".
|
||||
// (You'll need to add a space as well!)
|
||||
const major = "Major";
|
||||
const tom = "Tom";
|
||||
const major_tom = major ??? tom;
|
||||
|
||||
// That's all the problems. Let's see our results:
|
||||
std.debug.print("d={u} {}{}\n",.{d, laugh, major_tom});
|
||||
// Going deeper:
|
||||
//
|
||||
// Keen eyes will notice that we've put a 'u' inside the '{}'
|
||||
// placeholder in the format string above. This tells the
|
||||
// print() function (which uses std.fmt.format() function) to
|
||||
// print out a UTF-8 character. Otherwise we'd see '100', which
|
||||
// is the decimal number corresponding with the 'd' character
|
||||
// in UTF-8.
|
||||
// print() function to format the values as a UTF-8 character.
|
||||
// If we didn't do this, we'd see '100', which is the decimal
|
||||
// number corresponding with the 'd' character in UTF-8.
|
||||
//
|
||||
// While we're on this subject, 'c' (ASCII encoded character)
|
||||
// would work in place for 'u' because the first 128 characters
|
||||
// of UTF-8 are the same as ASCII!
|
||||
//
|
||||
}
|
||||
|
|
21
09_if.zig
21
09_if.zig
|
@ -1,19 +1,19 @@
|
|||
//
|
||||
// Now we get into the fun stuff, starting with the 'if' statement!
|
||||
//
|
||||
// if (true) {
|
||||
// // stuff
|
||||
// } else {
|
||||
// // other stuff
|
||||
// }
|
||||
// if (true) {
|
||||
// ...
|
||||
// } else {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// Zig has the usual comparison operators such as:
|
||||
// Zig has the "usual" comparison operators such as:
|
||||
//
|
||||
// a == b a equals b
|
||||
// a < b a is less than b
|
||||
// a !=b a does not equal b
|
||||
// a == b means "a equals b"
|
||||
// a < b means "a is less than b"
|
||||
// a !=b means "a does not equal b"
|
||||
//
|
||||
// The important thing about Zig's 'if' is that it *only* accepts
|
||||
// The important thing about Zig's "if" is that it *only* accepts
|
||||
// boolean values. It won't coerce numbers or other types of data
|
||||
// to true and false.
|
||||
//
|
||||
|
@ -22,6 +22,7 @@ const std = @import("std");
|
|||
pub fn main() void {
|
||||
const foo = 1;
|
||||
|
||||
// Please fix this condition:
|
||||
if (foo) {
|
||||
// We want out program to print this message!
|
||||
std.debug.print("Foo is 1!\n", .{});
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
//
|
||||
// If statements are also valid expressions:
|
||||
//
|
||||
// foo = if (a) 2 else 3;
|
||||
// var foo: u8 = if (a) 2 else 3;
|
||||
//
|
||||
// Note: you'll need to declare a variable type when assigning a value
|
||||
// Note: You'll need to declare a variable type when assigning a value
|
||||
// from a statement like this because the compiler isn't smart enough
|
||||
// to infer the type for you.
|
||||
//
|
||||
// This WON'T work:
|
||||
//
|
||||
// var foo = if (a) 2 else 3; // error!
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
var discount = true;
|
||||
|
||||
// Please use an if...else expression to set "price".
|
||||
// If discount is true, the price should be $17, otherwise $20:
|
||||
var price = if ???;
|
||||
|
||||
|
|
13
11_while.zig
13
11_while.zig
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// Zig 'while' statements create a loop that runs while the
|
||||
// condition is true:
|
||||
// condition is true. This runs once (at most):
|
||||
//
|
||||
// while (condition) {
|
||||
// condition = false;
|
||||
|
@ -10,16 +10,17 @@
|
|||
// that we can get a boolean value from conditional operators
|
||||
// such as:
|
||||
//
|
||||
// a == b a equals b
|
||||
// a < b a is less than b
|
||||
// a > b a is greater than b
|
||||
// a !=b a does not equal b
|
||||
// a == b means "a equals b"
|
||||
// a < b means "a is less than b"
|
||||
// a > b means "a is greater than b"
|
||||
// a !=b means "a does not equal b"
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
var n: u32 = 2;
|
||||
|
||||
// Please use a condition that is true UNTIL "n" reaches 1024:
|
||||
while ( ??? ){
|
||||
// Print the current number
|
||||
std.debug.print("{} ", .{n});
|
||||
|
@ -28,6 +29,6 @@ pub fn main() void {
|
|||
n *= 2;
|
||||
}
|
||||
|
||||
// Make this print n=1024
|
||||
// Once the above is correct, this will print "n=1024"
|
||||
std.debug.print("n={}\n", .{n});
|
||||
}
|
||||
|
|
|
@ -23,11 +23,13 @@ const std = @import("std");
|
|||
pub fn main() void {
|
||||
var n: u32 = 2;
|
||||
|
||||
// Please set the continue expression so that we get the desired
|
||||
// results in the print statement below.
|
||||
while (n < 1000) : ??? {
|
||||
// Print the current number
|
||||
std.debug.print("{} ", .{n});
|
||||
}
|
||||
|
||||
// Make this print n=1024
|
||||
// As in the last exercise, we want this to result in "n=1024"
|
||||
std.debug.print("n={}\n", .{n});
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
// Example:
|
||||
//
|
||||
// while (condition) : (continue expression){
|
||||
//
|
||||
// if(other condition) continue;
|
||||
// ...
|
||||
//
|
||||
// }
|
||||
//
|
||||
// The continue expression executes even when 'other condition'
|
||||
// is true and the loop is restarted by the 'continue' statement.
|
||||
// The "continue expression" executes every time the loop restarts
|
||||
// whether the "continue" statement happens or not.
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
//
|
||||
// Continue expressions do NOT execute when a while loop stops
|
||||
// because of a 'break' statement.
|
||||
//
|
||||
// Example:
|
||||
// You can force a loop to exit immediately with a "break" statement:
|
||||
//
|
||||
// while (condition) : (continue expression){
|
||||
//
|
||||
// if(other condition) break;
|
||||
// ...
|
||||
//
|
||||
// }
|
||||
//
|
||||
// Continue expressions do NOT execute when a while loop stops
|
||||
// because of a break!
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
var n: u32 = 1;
|
||||
|
||||
// Oh dear! This while loop will go forever!?
|
||||
// Please fix this so the print statement below gives the desired output.
|
||||
while (true) : (n+=1) {
|
||||
if(???) ???;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//
|
||||
// Behold the 'for' loop! It lets you execute code for each
|
||||
// member of an array (and things called 'slices' which we'll
|
||||
// get to in a bit).
|
||||
// member of an array:
|
||||
//
|
||||
// for (items) |item| {
|
||||
//
|
||||
// // Do something with item
|
||||
//
|
||||
// }
|
||||
//
|
||||
const std = @import("std");
|
||||
|
@ -22,3 +23,6 @@ pub fn main() void {
|
|||
|
||||
std.debug.print("The End.\n", .{});
|
||||
}
|
||||
//
|
||||
// Note that "for" loops also work on things called "slices"
|
||||
// which we'll see later.
|
||||
|
|
10
16_for2.zig
10
16_for2.zig
|
@ -3,9 +3,15 @@
|
|||
// number starting with 0 that counts up with each iteration:
|
||||
//
|
||||
// for (items) |item, index| {
|
||||
//
|
||||
// // Do something with item and index
|
||||
//
|
||||
// }
|
||||
//
|
||||
// You can name "item" and "index" anything you want. "i" is a popular
|
||||
// shortening of "index". The item name is often the singular form of
|
||||
// the items you're looping through.
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
|
@ -15,9 +21,9 @@ pub fn main() void {
|
|||
var value: u32 = 0;
|
||||
|
||||
// Now we'll convert the binary bits to a number value by adding
|
||||
// the value of the place as a power of two for each bit. See if
|
||||
// you can figure out the missing piece:
|
||||
// the value of the place as a power of two for each bit.
|
||||
//
|
||||
// See if you can figure out the missing piece:
|
||||
for (bits) |bit, ???| {
|
||||
var place_value = std.math.pow(u32, 2, @intCast(u32, i));
|
||||
value += place_value * bit;
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
//
|
||||
// Functions! FUNctions! FUN!
|
||||
// Functions! We've already seen a lot of one called "main()". Now let's try
|
||||
// writing one of our own:
|
||||
//
|
||||
// fn foo(n: u8) u8 {
|
||||
// return n+1;
|
||||
// }
|
||||
//
|
||||
// The foo() function above takes a number "n" and returns a number that is
|
||||
// larger by one.
|
||||
//
|
||||
// If your function doesn't take any parameters and doesn't return anything,
|
||||
// it would be defined like main():
|
||||
//
|
||||
// fn foo() void { }
|
||||
//
|
||||
const std = @import("std");
|
||||
|
||||
|
@ -11,12 +24,10 @@ pub fn main() void {
|
|||
}
|
||||
|
||||
//
|
||||
// We're just missing a couple things here. One thing we're NOT missing is the
|
||||
// keyword "pub", which is not needed here. Can you guess why?
|
||||
// Please define the deepThought() function below.
|
||||
//
|
||||
// Functions need to specify the type of value they return. The main() function
|
||||
// above has a special return type "void", which means it returns nothing. This
|
||||
// function returns something. What might that be?
|
||||
// We're just missing a couple things. One thing we're NOT missing is the
|
||||
// keyword "pub", which is not needed here. Can you guess why?
|
||||
//
|
||||
??? deepThought() ??? {
|
||||
return 42; // Number courtesy Douglas Adams
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
//
|
||||
// Now let's use a function that takes a parameter.
|
||||
// Now let's create a function that takes a parameter. Here's an
|
||||
// example that takes two parameters. As you can see, parameters
|
||||
// are declared just like an other types ("name": "type"):
|
||||
//
|
||||
// fn myFunction( number: u8, is_lucky: bool ) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
const std = @import( "std" );
|
||||
|
||||
|
@ -13,16 +19,13 @@ pub fn main() void {
|
|||
}
|
||||
|
||||
//
|
||||
// Oops! We seem to have forgotten something here. Function
|
||||
// parameters look like this:
|
||||
//
|
||||
// fn myFunction( number: u8, is_lucky: bool ) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// As you can see, we declare the type of the parameter, just
|
||||
// like we declare the types of variables, with a colon ":".
|
||||
// Please give this function the correct input parameter(s).
|
||||
// You'll need to figure out the parameter name and type that we're
|
||||
// expecting. The output type has already been specified for you.
|
||||
//
|
||||
fn twoToThe(???) u32 {
|
||||
return std.math.pow(u32, 2, my_number);
|
||||
// std.math.pow(type, a, b) takes a numeric type and two numbers
|
||||
// of that type and returns "a to the power of b" as that same
|
||||
// numeric type.
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue