Skip to content

Commit 847baf5

Browse files
bobahopdem4ron
authored andcommitted
secret-handshake: add approach (exercism#1636)
Add approach to `secret-handshake`
1 parent 975bbcd commit 847baf5

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"introduction": {
3+
"authors": ["bobahop"],
4+
"contributors": []
5+
},
6+
"approaches": [
7+
{
8+
"uuid": "cd0c0a31-0905-47f0-815c-02597a7cdf40",
9+
"slug": "iterate-once",
10+
"title": "Iterate once",
11+
"blurb": "Iterate once even when reversed.",
12+
"authors": ["bobahop"]
13+
}
14+
]
15+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Introduction
2+
3+
There are many ways to solve Secret Handshake.
4+
One approach is to iterate only once, even when the signs are to be reversed.
5+
6+
## General guidance
7+
8+
Something to consider is to keep the number of iterations at a minimum to get the best performance.
9+
However, if that is felt to adversely impact readability, then to use a series of `if` statements and then reverse is also valid.
10+
11+
## Approach: Iterate once
12+
13+
```rust
14+
const SIGNS: [&'static str; 4] = ["wink", "double blink", "close your eyes", "jump"];
15+
const REVERSE_SIGNS: u8 = 16;
16+
17+
pub fn actions(n: u8) -> Vec<&'static str> {
18+
let (mut action, action_incr, end) = match n & REVERSE_SIGNS {
19+
0 => (0, 1, 4),
20+
_ => (3, -1, -1),
21+
};
22+
let mut output: Vec<&'static str> = Vec::new();
23+
24+
loop {
25+
if action == end {
26+
break;
27+
}
28+
if (n & (1 << action)) != 0 {
29+
output.push(SIGNS[action as usize])
30+
}
31+
action += action_incr
32+
}
33+
output
34+
}
35+
```
36+
37+
For more information, check the [Iterate once approach][approach-iterate-once].
38+
39+
[approach-iterate-once]: https://exercism.org/tracks/rust/exercises/secret-handshake/approaches/iterate-once
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Iterate once
2+
3+
```rust
4+
const SIGNS: [&'static str; 4] = ["wink", "double blink", "close your eyes", "jump"];
5+
const REVERSE_SIGNS: u8 = 16;
6+
7+
pub fn actions(n: u8) -> Vec<&'static str> {
8+
let (mut action, action_incr, end) = match n & REVERSE_SIGNS {
9+
0 => (0, 1, 4),
10+
_ => (3, -1, -1),
11+
};
12+
let mut output: Vec<&'static str> = Vec::new();
13+
14+
loop {
15+
if action == end {
16+
break;
17+
}
18+
if (n & (1 << action)) != 0 {
19+
output.push(SIGNS[action as usize])
20+
}
21+
action += action_incr
22+
}
23+
output
24+
}
25+
```
26+
27+
This approach starts by defining a fixed-size [array][array] to hold the signal values in normal order.
28+
29+
The `[&'static str; 4]` is used to give the type and length of the array.
30+
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,
31+
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
32+
the elements itself.
33+
34+
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].
35+
36+
The `actions` function uses multiple assignment with a `match` expression to define the variables that control iterating through the signals array,
37+
setting their values to iterate in either the normal or reverse order.
38+
39+
The [bitwise AND operator][bitand] is used to check if the input number contains the signal for reversing the order of the other signals.
40+
41+
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.
42+
The `1` in `10000` is also at the same position in `10011`, so the two values ANDed will not be `0`.
43+
- `10011` AND
44+
- `10000` =
45+
- `10000`
46+
47+
If the number passed in is `3`, which is `00011` in binary, then it is ANDed with `16`, which is `10000` in binary.
48+
The `1` in `10000` is not at the same position in `00011`, so the two values ANDed will be `0`.
49+
- `00011` AND
50+
- `10000` =
51+
- `00000`
52+
53+
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
54+
in their normal order, otherwise they are set to iterate through the arrray backwards..
55+
56+
The output `vector` is defined, and then the [`loop`][loop] begins.
57+
58+
Normal iteration will start at index `0`.
59+
Reverse iteration will start at index `3`.
60+
61+
Normal iteration will terminate when the index equals `4`.
62+
Reverse iteration will terminate when the index equals `-1`.
63+
64+
Normal iteration will increase the index by `1` for each iteration.
65+
Reverse iteration will decrease the index by `1` for each iteration.
66+
67+
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
68+
as the value being iterated.
69+
70+
```rust
71+
if (n & (1 << action)) != 0 {
72+
output.push(SIGNS[action as usize])
73+
}
74+
```
75+
76+
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`.
77+
If the number passed in is `3`, which is `00011` in binary, then it is ANDed with `00001`.
78+
`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`.
79+
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`
80+
using the [push][push] function.
81+
82+
If the number being iterated is `1`, then `1` is shifted left `1` time, and the number passed in is ANDed with `00010`.
83+
If the number passed in is `3`, which is `00011` in binary, then it is ANDed with `00010`.
84+
`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`.
85+
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`.
86+
87+
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`.
88+
89+
After iterating through the array of signals is done, the output `vector` is returned from the function.
90+
91+
[array]: https://doc.rust-lang.org/std/primitive.array.html
92+
[const]: https://doc.rust-lang.org/std/keyword.const.html
93+
[magic-number]: https://en.wikipedia.org/wiki/Magic_number_(programming)
94+
[bitand]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html
95+
[shl]: https://doc.rust-lang.org/std/ops/trait.Shl.html
96+
[loop]: https://doc.rust-lang.org/rust-by-example/flow_control/loop.html
97+
[push]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.push
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
let (mut action, action_incr, end) = match n & REVERSE_SIGNS {
2+
0 => (0, 1, 4),
3+
_ => (3, -1, -1),
4+
};

0 commit comments

Comments
 (0)