Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maintain the signature of the recursive function identical to that of the loop #1852

Merged
merged 2 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions exercises/practice/binary-search/.approaches/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,19 @@ For more information, check the [Looping approach][approach-looping].
```rust
use std::cmp::Ordering;

fn find_rec<U: AsRef<[T]>, T: Ord>(array: U, key: T, offset: usize) -> Option<usize> {
fn find<U: AsRef<[T]>, T: Ord>(array: U, key: T) -> Option<usize> {
let array = array.as_ref();
if array.len() == 0 {
if array.is_empty() {
return None;
}
let mid = array.len() / 2;

match array[mid].cmp(&key) {
Ordering::Equal => Some(offset + mid),
Ordering::Less => find_rec(&array[mid + 1..], key, offset + mid + 1),
Ordering::Greater => find_rec(&array[..mid], key, offset),
Ordering::Greater => find(&array[..mid], key),
Ordering::Less => find(&array[mid + 1..], key).map(|p| p + mid + 1),
}
}

pub fn find<U: AsRef<[T]>, T: Ord>(array: U, key: T) -> Option<usize> {
find_rec(array, key, 0)
}
```

For more information, check the [Recursion approach][approach-recursion].
Expand Down
42 changes: 14 additions & 28 deletions exercises/practice/binary-search/.approaches/recursion/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,24 @@
```rust
use std::cmp::Ordering;

fn find_rec<U: AsRef<[T]>, T: Ord>(array: U, key: T, offset: usize) -> Option<usize> {
fn find<U: AsRef<[T]>, T: Ord>(array: U, key: T) -> Option<usize> {
let array = array.as_ref();
if array.len() == 0 {
if array.is_empty() {
return None;
}
let mid = array.len() / 2;

match array[mid].cmp(&key) {
Ordering::Equal => Some(offset + mid),
Ordering::Less => find_rec(&array[mid + 1..], key, offset + mid + 1),
Ordering::Greater => find_rec(&array[..mid], key, offset),
Ordering::Equal => Some(mid),
Ordering::Greater => find(&array[..mid], key),
Ordering::Less => find(&array[mid + 1..], key).map(|p| p + mid + 1),
}
}

pub fn find<U: AsRef<[T]>, T: Ord>(array: U, key: T) -> Option<usize> {
find_rec(array, key, 0)
}
```

This approach starts by using the [`Ordering`][ordering-enum] enum.

The `find_rec()` function has a signature to support the optional generic tests.
The `find()` function has a signature to support the optional generic tests.
To support slices, arrays and Vecs, which can be of varying lengths and sizes at runtime,
the compiler needs to be given informaton it can know at compile time.
A reference to any of those containers will always be of the same size (essentially the size of a pointer),
Expand All @@ -33,16 +29,11 @@ so [`AsRef`][asref] is used to constrain the generic type to be anything that is
The `<[T]>` is used to constrain the reference type to an indexable type `T`.
The `T` is constrained to be anything which implements the [`Ord`][ord] trait, which essentially means the values must be able to be ordered.

So, the `key` is of type `T` (orderable), and the `array` is of type `U` (a reference to an indexable container of orderable values
of the same type as the `key`.)

Since slices of the `array` will keep getting shorter with each recursive call to itself, `find_rec()` has an `offset` parameter
to keep track of the actual midpoint as it relates to the original `array`.
So, the `key` is of type `T` (orderable), and the `array` is of type `U` (a reference to an indexable container of orderable values of the same type as the `key`.)

Although `array` is defined as generic type `U`, which is constrained to be of type `AsRef`,
the [`as_ref()`][asref] method is used to get the reference to the actual type.
Without it, the compiler would complain that "no method named `len` found for type parameter `U` in the current scope" and
"cannot index into a value of type `U`".
Without it, the compiler would complain that "no method named `len` found for type parameter `U` in the current scope" and "cannot index into a value of type `U`".

If the `array` is empty, then [`None`][none] is returned.

Expand All @@ -52,19 +43,14 @@ Since the element is a reference, the `key` must also be referenced.

The [`match`][match] arms each use a value from the `Ordering` enum.

- If the midpoint element value equals the `key`, then the midpoint plus the offset is returned from the function wrapped in a [`Some`][some].
- If the midpoint element value is less than the `key`, then `find_rec()` calls itself,
passing a slice of the `array` from the element to the right of the midpoint through the end of the `array`.
The offset is adjusted to be itself plus the midpoint plus `1`.
- If the midpoint element value is greater than the `key`, then `find_rec()` calls itself,
- If the midpoint element value equals the `key`, then the midpoint is returned from the function wrapped in a [`Some`][some].
- If the midpoint element value is greater than the `key`, then `find()` calls itself,
passing a slice of the `array` from the beginning up to but not including the midpoint element.
The offset remains as is.

While the element value is not equal to the `key`, `find_rec()` keeps calling itself while halving the number of elements being searched,
until either the `key` is found, or, if it is not in the `array`, the `array` is whittled down to empty.
- If the midpoint element value is less than the `key`, then `find()` calls itself,
passing a slice of the `array` from the element to the right of the midpoint through the end of the `array`.
The return postion from the recursive call is the midpoint start from the new left(`mid + 1`), so we add `mid + 1` to the return postion.

The `find()` method returns the final result from calling the `find_rec()` method, passing in the `array`, `key`, and `0` for the initial
offset value.
While the element value is not equal to the `key`, `find()` keeps calling itself while halving the number of elements being searched, until either the `key` is found, or, if it is not in the `array`, the `array` is whittled down to empty.

[ordering-enum]: https://doc.rust-lang.org/std/cmp/enum.Ordering.html
[asref]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
let mid = array.len() / 2;

match array[mid].cmp(&key) {
Ordering::Equal => Some(offset + mid),
Ordering::Less => find_rec(&array[mid + 1..], key, offset + mid + 1),
Ordering::Greater => find_rec(&array[..mid], key, offset),
Ordering::Equal => Some(mid),
Ordering::Greater => find(&array[..mid], key),
Ordering::Less => find(&array[mid + 1..], key).map(|p| p + mid + 1),
}