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
Secret Handshake approach #1636
Merged
ErikSchierboom
merged 13 commits into
exercism:main
from
bobahop:secret-handshake-approach
Mar 3, 2023
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
0d4ecd3
Create config.json
bobahop 564e5e8
Update config.json
bobahop 1f33902
Create introduction.md
bobahop fda42ad
Update introduction.md
bobahop c484d3b
Update introduction.md
bobahop 63f4eb9
Create snippet.txt
bobahop ee7ce3f
Rename exercises/practice/secret-handshake/.approaches/snippet.txt to…
bobahop 32a795c
Create content.md
bobahop a2594db
Update config.json
bobahop 5458c10
Update introduction.md
bobahop 76d5a26
Update snippet.txt
bobahop 1b01eb0
Update content.md
bobahop afd37ee
Update introduction.md
bobahop File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
exercises/practice/secret-handshake/.approaches/config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"introduction": { | ||
"authors": ["bobahop"], | ||
"contributors": [] | ||
}, | ||
"approaches": [ | ||
{ | ||
"uuid": "cd0c0a31-0905-47f0-815c-02597a7cdf40", | ||
"slug": "iterate-once", | ||
"title": "Iterate once", | ||
"blurb": "Iterate once even when reversed.", | ||
"authors": ["bobahop"] | ||
} | ||
] | ||
} |
39 changes: 39 additions & 0 deletions
39
exercises/practice/secret-handshake/.approaches/introduction.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Introduction | ||
|
||
There are many ways to solve Secret Handshake. | ||
One approach is to iterate only once, even when the signs are to be reversed. | ||
|
||
## General guidance | ||
|
||
Something to consider is to keep the number of iterations at a minimum to get the best performance. | ||
However, if that is felt to adversely impact readability, then to use a series of `if` statements and then reverse is also valid. | ||
|
||
## Approach: Iterate once | ||
|
||
```rust | ||
const SIGNS: [&'static str; 4] = ["wink", "double blink", "close your eyes", "jump"]; | ||
const REVERSE_SIGNS: u8 = 16; | ||
|
||
pub fn actions(n: u8) -> Vec<&'static str> { | ||
let (mut action, action_incr, end) = match n & REVERSE_SIGNS { | ||
0 => (0, 1, 4), | ||
_ => (3, -1, -1), | ||
}; | ||
let mut output: Vec<&'static str> = Vec::new(); | ||
|
||
loop { | ||
if action == end { | ||
break; | ||
} | ||
if (n & (1 << action)) != 0 { | ||
output.push(SIGNS[action as usize]) | ||
} | ||
action += action_incr | ||
} | ||
output | ||
} | ||
``` | ||
|
||
For more information, check the [Iterate once approach][approach-iterate-once]. | ||
|
||
[approach-iterate-once]: https://exercism.org/tracks/rust/exercises/secret-handshake/approaches/iterate-once |
97 changes: 97 additions & 0 deletions
97
exercises/practice/secret-handshake/.approaches/iterate-once/content.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# Iterate once | ||
|
||
```rust | ||
const SIGNS: [&'static str; 4] = ["wink", "double blink", "close your eyes", "jump"]; | ||
const REVERSE_SIGNS: u8 = 16; | ||
|
||
pub fn actions(n: u8) -> Vec<&'static str> { | ||
let (mut action, action_incr, end) = match n & REVERSE_SIGNS { | ||
0 => (0, 1, 4), | ||
_ => (3, -1, -1), | ||
}; | ||
let mut output: Vec<&'static str> = Vec::new(); | ||
|
||
loop { | ||
if action == end { | ||
break; | ||
} | ||
if (n & (1 << action)) != 0 { | ||
output.push(SIGNS[action as usize]) | ||
} | ||
action += action_incr | ||
} | ||
output | ||
} | ||
``` | ||
|
||
This approach starts by defining a fixed-size [array][array] to hold the signal values in normal order. | ||
|
||
The `[&'static str; 4]` is used to give the type and length of the array. | ||
To be a [`const`][const], the size of the array must be known at compile time, so setting the type and length must be done explicitly, | ||
so the size in bytes of the fixed array can be deduced by the compiler from that and not by inspecting the element types and counting | ||
the elements itself. | ||
|
||
The value of `16` is defined as a `const` with a meaningful name so it won't be used as a [magic number][magic-number]. | ||
|
||
The `actions` function uses multiple assignment with a `match` expression to define the variables that control iterating through the signals array, | ||
setting their values to iterate in either the normal or reverse order. | ||
|
||
The [bitwise AND operator][bitand] is used to check if the input number contains the signal for reversing the order of the other signals. | ||
|
||
For example, if the number passed in is `19`, which is `10011` in binary, then it is ANDed with `16`, which is `10000` in binary. | ||
The `1` in `10000` is also at the same position in `10011`, so the two values ANDed will not be `0`. | ||
- `10011` AND | ||
- `10000` = | ||
- `10000` | ||
|
||
If the number passed in is `3`, which is `00011` in binary, then it is ANDed with `16`, which is `10000` in binary. | ||
The `1` in `10000` is not at the same position in `00011`, so the two values ANDed will be `0`. | ||
- `00011` AND | ||
- `10000` = | ||
- `00000` | ||
|
||
If the number passed in does not contain the signal for reverse, then the iteration variables are set to iterate through the array of signals | ||
in their normal order, otherwise they are set to iterate through the arrray backwards.. | ||
|
||
The output `vector` is defined, and then the [`loop`][loop] begins. | ||
|
||
Normal iteration will start at index `0`. | ||
Reverse iteration will start at index `3`. | ||
|
||
Normal iteration will terminate when the index equals `4`. | ||
Reverse iteration will terminate when the index equals `-1`. | ||
|
||
Normal iteration will increase the index by `1` for each iteration. | ||
Reverse iteration will decrease the index by `1` for each iteration. | ||
|
||
For each iteration of the `loop`, the AND operator is used to check if the number passed in contains `1` [shifted left][shl] (`<<`) for the number of positions | ||
as the value being iterated. | ||
|
||
```rust | ||
if (n & (1 << action)) != 0 { | ||
output.push(SIGNS[action as usize]) | ||
} | ||
``` | ||
|
||
For example, if the number being iterated is `0`, then `1` is shifted left `0` times (so not shifted at all), and the number passed in is ANDed with `00001`. | ||
If the number passed in is `3`, which is `00011` in binary, then it is ANDed with `00001`. | ||
`00011` ANDed with `00001` is not equal to `0`, so the signal at the index of the array of signals is added to the output `vector`. | ||
The index used is the number being iterated, which is `0`, so the element at index `0` (`"wink"`) would be added to the output `vector` | ||
using the [push][push] function. | ||
|
||
If the number being iterated is `1`, then `1` is shifted left `1` time, and the number passed in is ANDed with `00010`. | ||
If the number passed in is `3`, which is `00011` in binary, then it is ANDed with `00010`. | ||
`00011` ANDed with `00010` is not equal to `0`, so the signal at the index of the array of signals is added to the output `vector`. | ||
The index used is the number being iterated, which is `1`, so the element at index `1` (`"double blink"`) would be added to the output `vector`. | ||
|
||
If the number passed in ANDed with the number being iterated is equal to `0`, then the signal in the array for that index is not added to the output `vector`. | ||
|
||
After iterating through the array of signals is done, the output `vector` is returned from the function. | ||
|
||
[array]: https://doc.rust-lang.org/std/primitive.array.html | ||
[const]: https://doc.rust-lang.org/std/keyword.const.html | ||
[magic-number]: https://en.wikipedia.org/wiki/Magic_number_(programming) | ||
[bitand]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html | ||
[shl]: https://doc.rust-lang.org/std/ops/trait.Shl.html | ||
[loop]: https://doc.rust-lang.org/rust-by-example/flow_control/loop.html | ||
[push]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.push |
4 changes: 4 additions & 0 deletions
4
exercises/practice/secret-handshake/.approaches/iterate-once/snippet.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
let (mut action, action_incr, end) = match n & REVERSE_SIGNS { | ||
0 => (0, 1, 4), | ||
_ => (3, -1, -1), | ||
}; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must admit that I'm not well-versed in Rust, but to me this is very hard to read. It seems to almost obscure the logic. Is this an idiomatic way to solve the exercise in Rust? What about something simple like: https://exercism.org/tracks/rust/exercises/secret-handshake/solutions/aciba90?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are simpler approaches which don't offer as much in pedagogic value. Since I only did one approach, I used one which is not exactly unidiomatic, but which afforded more in the way of explanation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Okay!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is an interesting approach