From adf5ddb27df7f5a22b0b7d3321dfc8bca1e7937a Mon Sep 17 00:00:00 2001 From: Dave Gauer Date: Sun, 7 Feb 2021 11:06:51 -0500 Subject: [PATCH] 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. --- 01_hello.zig | 14 ++++++++++---- 02_std.zig | 15 +++++++++------ 03_assignment.zig | 36 +++++++++++++++++++++++++++--------- 04_arrays.zig | 34 ++++++++++++++++++++++++++-------- 05_arrays2.zig | 19 +++++++++++++------ 06_strings.zig | 26 +++++++++++++++++--------- 09_if.zig | 21 +++++++++++---------- 10_if2.zig | 9 +++++++-- 11_while.zig | 13 +++++++------ 12_while2.zig | 4 +++- 13_while3.zig | 7 ++++--- 14_while4.zig | 12 +++++++----- 15_for.zig | 8 ++++++-- 16_for2.zig | 10 ++++++++-- 18_functions.zig | 23 +++++++++++++++++------ 19_functions2.zig | 23 +++++++++++++---------- 16 files changed, 185 insertions(+), 89 deletions(-) diff --git a/01_hello.zig b/01_hello.zig index d61a999..8d26940 100644 --- a/01_hello.zig +++ b/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 ..." +// +// Zig functions are private by default but the main() function should +// be public. // -// Try to fix the program and run `ziglings` to see if it passes. +// 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"); diff --git a/02_std.zig b/02_std.zig index 62ce040..dcc1b87 100644 --- a/02_std.zig +++ b/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", .{}); diff --git a/03_assignment.zig b/03_assignment.zig index 2c4c15a..d26f2a2 100644 --- a/03_assignment.zig +++ b/03_assignment.zig @@ -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"); diff --git a/04_arrays.zig b/04_arrays.zig index a509800..0f4ffe1 100644 --- a/04_arrays.zig +++ b/04_arrays.zig @@ -1,31 +1,49 @@ // // Let's learn some array basics. Arrays are declared with: // -// const foo [size] = [size]{ 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 = [_]{ 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", diff --git a/05_arrays2.zig b/05_arrays2.zig index c021096..9282a31 100644 --- a/05_arrays2.zig +++ b/05_arrays2.zig @@ -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| { diff --git a/06_strings.zig b/06_strings.zig index cac40e0..2430884 100644 --- a/06_strings.zig +++ b/06_strings.zig @@ -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! + // } diff --git a/09_if.zig b/09_if.zig index 3309cbf..28ac712 100644 --- a/09_if.zig +++ b/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", .{}); diff --git a/10_if2.zig b/10_if2.zig index 5e78d54..4f559cd 100644 --- a/10_if2.zig +++ b/10_if2.zig @@ -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 ???; diff --git a/11_while.zig b/11_while.zig index 820cf56..4c4fc4f 100644 --- a/11_while.zig +++ b/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}); } diff --git a/12_while2.zig b/12_while2.zig index dba1a26..6f808c8 100644 --- a/12_while2.zig +++ b/12_while2.zig @@ -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}); } diff --git a/13_while3.zig b/13_while3.zig index 55fcc0a..3ff42ff 100644 --- a/13_while3.zig +++ b/13_while3.zig @@ -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"); diff --git a/14_while4.zig b/14_while4.zig index e686f88..a28b9a9 100644 --- a/14_while4.zig +++ b/14_while4.zig @@ -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(???) ???; } diff --git a/15_for.zig b/15_for.zig index 51ab67f..652478b 100644 --- a/15_for.zig +++ b/15_for.zig @@ -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. diff --git a/16_for2.zig b/16_for2.zig index 4b01b86..0a62a1a 100644 --- a/16_for2.zig +++ b/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; diff --git a/18_functions.zig b/18_functions.zig index ad97585..bda90cd 100644 --- a/18_functions.zig +++ b/18_functions.zig @@ -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 diff --git a/19_functions2.zig b/19_functions2.zig index 68cc67b..4d195a7 100644 --- a/19_functions2.zig +++ b/19_functions2.zig @@ -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. }