diff --git a/config.json b/config.json index 5609f8e10..ac51e258f 100644 --- a/config.json +++ b/config.json @@ -35,6 +35,22 @@ }, "exercises": { "concept": [ + { + "slug": "bird-watcher", + "uuid": "ef6bdeea-56d5-44d9-8971-1ac3b3a8f624", + "name": "Bird Watcher", + "difficulty": 1, + "concepts": [ + "for-loops", + "ranges", + "arrays" + ], + "prerequisites": [ + "if-statements", + "assignment" + ], + "status": "wip" + }, { "slug": "lucians-luscious-lasagna", "uuid": "29a2d3bd-eec8-454d-9dba-4b2d7d071925", diff --git a/exercises/concept/bird-watcher/.docs/hints.md b/exercises/concept/bird-watcher/.docs/hints.md new file mode 100644 index 000000000..aaedf2f9e --- /dev/null +++ b/exercises/concept/bird-watcher/.docs/hints.md @@ -0,0 +1,44 @@ +# Hints + +## General + +- The bird count per day is an array that contains exactly 7 integers. + +## 1. Check what the counts were last week + +- Define an array that contains last week's log. +- Return the array as a result. + +## 2. Check how many birds visited today + +- Remember that the counts are ordered by day from oldest to most recent, with the last element representing today. +- Accessing the last element can be done by using its (fixed) index (remember to start counting from zero). + +## 3. Increment today's count + +- Set the element representing today's count to today's count plus 1. + +## 4. Check if there was a day with no visiting birds + +- Use if statements for performing comparisions with array elements. +- Use `for in` construct to loop over an array. + +## 5. Calculate the number of visiting birds for the first x number of days + +- A variable can be used to hold the count for the number of visiting birds. +- The array can be _enumerated_ over using [`.iter()`][.iter()] and [`.enumerate()`][.enumerate()] methods. +- The variable can be updated inside the loop. +- Remember: arrays are indexed from `0`. +- Use `break` statement to stop a loop. + +## 6. Calculate the number of busy days + +- A variable can be used to hold the number of busy days. +- The array can be _iterated_ over using [`.iter()`][.iter()] method. +- The variable can be updated inside the loop. +- A [conditional statement][if-statement] can be used inside the loop. + +[if-statement]: https://doc.rust-lang.org/rust-by-example/flow_control/if_else.html +[.iter()]: https://doc.rust-lang.org/rust-by-example/flow_control/for.html?highlight=iter()#for-and-iterators +[.enumerate()]: + https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.enumerate diff --git a/exercises/concept/bird-watcher/.docs/instructions.md b/exercises/concept/bird-watcher/.docs/instructions.md new file mode 100644 index 000000000..0d832f6a0 --- /dev/null +++ b/exercises/concept/bird-watcher/.docs/instructions.md @@ -0,0 +1,68 @@ +# Instructions + +You're an avid bird watcher that keeps track of how many birds have visited your garden in the last seven days. + +You have six tasks, all dealing with the numbers of birds that visited your garden. + +## 1. Check what the counts were last week + +For comparison purposes, you always keep a copy of last week's log nearby, +which were: 0, 2, 5, 3, 7, 8 and 4. Implement the `last_week_log()` function +that returns last week's log + +```rust +last_week_log(); +// => [0, 2, 5, 3, 7, 8, 4] +``` + +## 2. Check how many birds visited today + +Implement the `count_today()` function to return how many birds visited your garden today. The bird counts are ordered by day, with the first element being the count of the oldest day, and the last element being today's count. + +```rust +let watch_log = [ 2, 5, 0, 7, 4, 1 ]; +count_today(watch_log); +// => 1 +``` + +## 3. Increment today's count + +Implement the `log_today()` function to increment today's count: + +```rust +let watch_log = [ 2, 5, 0, 7, 4, 1 ]; +log_today(watch_log); +count_today(watch_log); +// => 2 +``` + +## 4. Check if there was a day with no visiting birds + +Implement the `has_day_without_birds()` method that returns `true` if there was a day at which zero birds visited the garden; otherwise, return `false`: + +```rust +let watch_log = [ 2, 5, 0, 7, 4, 1 ]; +has_day_without_birds(watch_log); +// => true +``` + +## 5. Calculate the number of visiting birds for the first x number of days + +Implement the `tally_days()` function that returns the number of birds that have visited your garden from the start of the week, but limit the count to the specified number of days from the start of the week. + +```rust +let watch_log = [ 2, 5, 0, 7, 4, 1 ]; +tally_days(watch_log, 4); +// => 14 +``` + +## 6. Calculate the number of busy days + +Some days are busier that others. A busy day is one where five or more birds have visited your garden. +Implement the `calc_busy_days()` function to return the number of busy days: + +```rust +let watch_log = [ 2, 5, 0, 7, 4, 1 ]; +calc_busy_days(watch_log); +// => 2 +``` diff --git a/exercises/concept/bird-watcher/.docs/introduction.md b/exercises/concept/bird-watcher/.docs/introduction.md new file mode 100644 index 000000000..31536becd --- /dev/null +++ b/exercises/concept/bird-watcher/.docs/introduction.md @@ -0,0 +1,263 @@ +# Introduction + +An `array` is a collection of objects of the same type `T`, stored in contiguous +memory. Arrays are created using brackets `[]`, and their length, which is known +at compile time, is part of their type signature `[T; length]`(...) + +Unlike arrays in some other languages, arrays in Rust have a fixed length. + + +## Arrays +### Defining an array +We write the values in an array as a comma-separated list inside square +brackets: + +```rust +let my_array = [1, 2, 3, 4, 5]; +``` + +We write an array’s type using square brackets with the type of each element, a +semicolon, and then the number of elements in the array, like so: + +```rust +let my_array: [i32; 5] = [1, 2, 3, 4, 5]; +``` + +Here, `i32` is the type of each element. After the semicolon, the number 5 +indicates the array contains five elements. + +You can also initialize an array to contain the same value for each element by +specifying the initial value, followed by a semicolon, and then the length of +the array in square brackets, as shown here: + +```rust +let my_array = [3; 5]; +// => [3, 3, 3, 3, 3] +``` + +### Accessing Array Elements +You can access elements of an array using indexing, like this: + +```rust +let a = [1, 2, 3, 4, 5]; + +let first = a[0]; +let second = a[1]; +``` + + +### Invalid Array Element Access +Let’s see what happens if you try to access an element of an array that is past +the end of the array. + +```rust +let a = [1, 2, 3, 4, 5]; + +let sixth = a[5]; +``` + +The above program will fail to compile with an error similar to: +```txt +let value = a[5]; + ^^^^^^^^^^^^ index out of bounds: the length is 5 but the index is 5 +``` + +Now consider when you don't know the index you want to access and it is provided to you via a +function parameter or a user input: + +```rust +fn access_element(index: usize) -> i32 { + let a = [1, 2, 3, 4, 5]; + return a[index]; +} + +fn main() { + access_element(5); +} +``` + +In this case, the Rust compiler cannot know +if the index is out of bounds. The code will compile without errors but fail at +runtime with the following error. + +```txt +thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 5' +``` + + +### Use as a type + +An array type can be used as a variable type, function parameter or return type. + +```rust +let my_var: [i32; 5]; + +fn my_func(input: [i32; 5]) -> [i32; 5] {} +``` + +### Changing array elements +Like for any other variable, arrays also have to be defined using `mut` keyword +to allow change. An element can be changed by using assignment operator while +accessing it via an index. + +```rust +let mut my_array = [2, 2, 3]; +my_array[0] = 1; +// => [1, 2, 3] +``` + +In the above example, we access the first element via it's index 0 and assign a +new value to it. + +## For loops + +It’s often useful to execute a block of code more than once. For this task, Rust +provides several loops, which will run through the code inside the loop body to +the end and then start immediately back at the beginning. + +### `for` and range + +The `for in` construct can be used to iterate through an `Iterator`. We will +learn more about `Iterators` in later concepts. One of the easiest ways to create an iterator is to use the range notation +`a..b`. This yields values from a (inclusive) to b (exclusive) in steps of one. + +```rust +// A for loop that iterates 10 times +for n in 0..10 { + // n will have a starting value of 0 + // n will have the last value as 9 +} +``` + +Alternatively, `a..=b` can be used for a range that is inclusive on both ends. The above can be written as: + +```rust +// A for loop that iterates 10 times +for n in 1..=10 { + // n will have a starting value of 1 + // n will have the last value as 10 +} +``` + +### Break and Continue a loop +A for loop can be halted by using the `break` statement. + +```rust +// A for loop that iterates 10 times +for n in 1..=10 { + println!(n); + break; +} +// => 1 +``` + +One can conditionally break a for loop as follows: + +```rust +// A for loop that iterates 10 times +for n in 1..=10 { + println!(n); + if n == 5 { + break; + } +} +// => 1 +// => 2 +// => 3 +// => 4 +// => 5 +``` + +A `continue` statement can be used to skip over an iteration. +```rust +// A for loop that iterates 10 times +for n in 1..=10 { + println!(n); + continue; +} +// => 1 +``` + +Similar to `break`, one can also conditionally use `continue`: + +```rust +// A for loop that iterates 10 times +for n in 1..=10 { + println!(n); + if n >= 5 { + continue; + } +} +// => 1 +// => 2 +// => 3 +// => 4 +// => 5 +``` + +### Looping Through an Array with `for` +The following shows an example of looping through an array using the `for in` construct. + +```rust +let a = [10, 20, 30, 40, 50]; + +for element in a { + println!("the value is: {element}"); +} +``` + +## Iterators + +The `for in` construct interacts in several ways with `Iterator`s. An array +implements the `Iterator` trait. Which makes it an iterator. All iterators have some +very useful built-in methods. You will learn more about what a trait is in later +concepts. For now let's explore the various methods. + +- `.iter()` allows to access each element of a collection and leaving the collection untouched and available for reuse after the loop. + ```rust + let a = ["Bob", "Frank", "Ferris"]; + + for name in a.iter() { + println!(name); + } + // => Bob + // => Frank + // => Ferris + + println!(a); + // => ["Bob", "Frank", "Ferris"] + ``` + +- `.rev()` allows to access each element in reverse order + ```rust + let a = ["Bob", "Frank", "Ferris"]; + + for name in a.iter().rev() { + println!(name); + } + // => Ferris + // => Frank + // => Bob + + println!(a); + // => ["Bob", "Frank", "Ferris"] + ``` + Notice that we still used `.iter()`. Thats because arrays have a `.reverse()` + method available which as the name suggest reverses the array. But this + changes the array. In our example we only intended to traverse the array in + reverse order not actually change. + +- `.enumerate()` allows to access each element along with it's index + ```rust + let a = ["Bob", "Frank", "Ferris"]; + + for (i, name) in a.iter().enumerate() { + println!("{}: {}", i, name); + } + // => 0: Bob + // => 1: Frank + // => 2: Ferris + + println!(a); + // => ["Bob", "Frank", "Ferris"] + ``` diff --git a/exercises/concept/bird-watcher/.gitignore b/exercises/concept/bird-watcher/.gitignore new file mode 100644 index 000000000..db7f315c0 --- /dev/null +++ b/exercises/concept/bird-watcher/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/exercises/concept/bird-watcher/.meta/config.json b/exercises/concept/bird-watcher/.meta/config.json new file mode 100644 index 000000000..8860a4915 --- /dev/null +++ b/exercises/concept/bird-watcher/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "devkabiir" + ], + "files": { + "solution": [ + "src/lib.rs", + "Cargo.toml" + ], + "test": [ + "tests/bird-watcher.rs" + ], + "exemplar": [ + ".meta/exemplar.rs" + ] + }, + "blurb": "Learn about for loops and arrays by keeping track of how many birds visit your garden." +} diff --git a/exercises/concept/bird-watcher/.meta/design.md b/exercises/concept/bird-watcher/.meta/design.md new file mode 100644 index 000000000..d20c23ce1 --- /dev/null +++ b/exercises/concept/bird-watcher/.meta/design.md @@ -0,0 +1,28 @@ +# Design + +## Learning objectives + +- Know how to define arrays +- Know how to access array elements +- Know how to change array elements +- Know how to loop over arrays +- Know how to enumerate arrays +- Know `for in` syntax +- Know how to create ranges + +## Out of scope + +- `slices` +- `traits` +- `Iterator` + +## Concepts + +- `for-loop` +- `ranges` +- `arrays` + +## Prerequisites + +- `if-statements` +- `assignment` diff --git a/exercises/concept/bird-watcher/.meta/exemplar.rs b/exercises/concept/bird-watcher/.meta/exemplar.rs new file mode 100644 index 000000000..305e74dcf --- /dev/null +++ b/exercises/concept/bird-watcher/.meta/exemplar.rs @@ -0,0 +1,46 @@ +pub fn last_week_log() -> [i32; 7] { + return [0, 2, 5, 3, 7, 8, 4]; +} + +pub fn count_today(watch_log: [i32; 7]) -> i32 { + return watch_log[6]; +} + +pub fn log_today(watch_log: &mut [i32; 7]) { + watch_log[6] += 1; +} + +pub fn has_day_without_birds(watch_log: [i32; 7]) -> bool { + for count in watch_log { + if count == 0 { + return true; + } + } + + return false; +} + +pub fn tally_days(watch_log: [i32; 7], days: usize) -> i32 { + let mut total = 0; + + for (i, count) in watch_log.iter().enumerate() { + if i >= days { + break; + } + total += count; + } + + return total; +} + +pub fn calc_busy_days(watch_log: [i32; 7]) -> u8 { + let mut days = 0; + + for count in watch_log { + if count >= 5 { + days += 1; + } + } + + return days; +} diff --git a/exercises/concept/bird-watcher/Cargo.toml b/exercises/concept/bird-watcher/Cargo.toml new file mode 100644 index 000000000..f2973a4aa --- /dev/null +++ b/exercises/concept/bird-watcher/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "bird_watcher" +version = "0.1.0" +edition = "2021" diff --git a/exercises/concept/bird-watcher/src/lib.rs b/exercises/concept/bird-watcher/src/lib.rs new file mode 100644 index 000000000..abe89d90a --- /dev/null +++ b/exercises/concept/bird-watcher/src/lib.rs @@ -0,0 +1,23 @@ +pub fn last_week_log() -> [i32; 7] { + unimplemented!("Implement last_week_log") +} + +pub fn count_today(_watch_log: [i32; 7]) -> i32 { + unimplemented!("Implement count_today") +} + +pub fn log_today(_watch_log: &mut [i32; 7]) { + unimplemented!("Implement log_today") +} + +pub fn has_day_without_birds(_watch_log: [i32; 7]) -> bool { + unimplemented!("Implement has_day_without_birds") +} + +pub fn tally_days(_watch_log: [i32; 7], _days: usize) -> i32 { + unimplemented!("Implement tally_days") +} + +pub fn calc_busy_days(_watch_log: [i32; 7]) -> u8 { + unimplemented!("Implement calc_busy_days") +} diff --git a/exercises/concept/bird-watcher/tests/bird_watcher.rs b/exercises/concept/bird-watcher/tests/bird_watcher.rs new file mode 100644 index 000000000..ee50f382f --- /dev/null +++ b/exercises/concept/bird-watcher/tests/bird_watcher.rs @@ -0,0 +1,82 @@ +#[test] +pub fn last_week() { + assert_eq!([0, 2, 5, 3, 7, 8, 4], bird_watcher::last_week_log()); +} + +#[test] +#[ignore] +pub fn today_for_disappointing_day() { + let bird_watch_log = [0, 0, 1, 0, 0, 1, 0]; + assert_eq!(0, bird_watcher::count_today(bird_watch_log)); +} + +#[test] +#[ignore] +pub fn today_for_busy_day() { + let bird_watch_log = [8, 8, 9, 5, 4, 7, 10]; + assert_eq!(10, bird_watcher::count_today(bird_watch_log)); +} + +#[test] +#[ignore] +pub fn increment_todays_count_with_no_previous_visits() { + let mut bird_watch_log = [0, 0, 0, 4, 2, 3, 0]; + bird_watcher::log_today(&mut bird_watch_log); + assert_eq!(1, bird_watcher::count_today(bird_watch_log)); +} + +#[test] +#[ignore] +pub fn increment_todays_count_with_multiple_previous_visits() { + let mut bird_watch_log = [8, 8, 9, 2, 1, 6, 4]; + bird_watcher::log_today(&mut bird_watch_log); + assert_eq!(5, bird_watcher::count_today(bird_watch_log)); +} + +#[test] +#[ignore] +pub fn has_day_without_birds_with_day_without_birds() { + let bird_watch_log = [5, 5, 4, 0, 7, 6, 7]; + + assert!(bird_watcher::has_day_without_birds(bird_watch_log)); +} + +#[test] +#[ignore] +pub fn has_day_without_birds_with_no_day_without_birds() { + let bird_watch_log = [4, 5, 9, 10, 9, 4, 3]; + + assert!(!bird_watcher::has_day_without_birds(bird_watch_log)); +} + +#[test] +#[ignore] +pub fn count_for_first_three_days_of_disappointing_week() { + let bird_watch_log = [0, 0, 1, 0, 0, 1, 0]; + + assert_eq!(1, bird_watcher::tally_days(bird_watch_log, 3)); +} + +#[test] +#[ignore] +pub fn count_for_first_six_days_of_busy_week() { + let bird_watch_log = [5, 9, 12, 6, 8, 8, 17]; + + assert_eq!(48, bird_watcher::tally_days(bird_watch_log, 6)); +} + +#[test] +#[ignore] +pub fn busy_days_for_disappointing_week() { + let bird_watch_log = [1, 1, 1, 0, 0, 0, 0]; + + assert_eq!(0, bird_watcher::calc_busy_days(bird_watch_log)); +} + +#[test] +#[ignore] +pub fn busy_days_for_busy_week() { + let bird_watch_log = [4, 9, 5, 7, 8, 8, 2]; + + assert_eq!(5, bird_watcher::calc_busy_days(bird_watch_log)); +}