-
Notifications
You must be signed in to change notification settings - Fork 543
Add a fourth approach to the rust version of the Bob problem #2058
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
2b06a52
d1eec13
bad88c5
269a110
f45e484
b028618
febe656
6b244df
caba2c4
1468d73
10e8201
b6ed43d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "code" | ||
version = "0.1.0" | ||
edition = "2024" | ||
|
||
[dependencies] | ||
|
||
[[bin]] | ||
name = "code" | ||
path = "main.rs" |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -6,6 +6,7 @@ fn main() { | |||||
println!("Hello, world!"); | ||||||
} | ||||||
|
||||||
// Reply using match | ||||||
pub fn reply_match(msg: &str) -> &str { | ||||||
let message = msg.trim_end(); | ||||||
if message.is_empty() { | ||||||
|
@@ -24,6 +25,7 @@ pub fn reply_match(msg: &str) -> &str { | |||||
} | ||||||
} | ||||||
|
||||||
// Reply using if chain | ||||||
pub fn reply_if_chain(msg: &str) -> &str { | ||||||
let message = msg.trim_end(); | ||||||
if message.is_empty() { | ||||||
|
@@ -47,6 +49,7 @@ pub fn reply_if_chain(msg: &str) -> &str { | |||||
"Whatever." | ||||||
} | ||||||
|
||||||
// Reply using array | ||||||
const ANSWERS: &'static [&'static str] = &[ | ||||||
"Whatever.", | ||||||
"Sure.", | ||||||
|
@@ -71,20 +74,132 @@ pub fn reply_array(msg: &str) -> &str { | |||||
ANSWERS[is_questioning + is_yelling] | ||||||
} | ||||||
|
||||||
// Reply using state machine | ||||||
enum State { | ||||||
Initial, | ||||||
HasQuestionMark, | ||||||
NoQuestionMarkUpperCase, | ||||||
NoQuestionMarkUndefined, | ||||||
QuestionMarkUpperCase, | ||||||
QuestionMarkUndefined, | ||||||
} | ||||||
|
||||||
const FINE_BE_THAT_WAY: &str = "Fine. Be that way!"; | ||||||
const WHATEVER: &str = "Whatever."; | ||||||
const SURE: &str = "Sure."; | ||||||
const CHILL_OUT: &str = "Whoa, chill out!"; | ||||||
const CALM_DOWN: &str = "Calm down, I know what I'm doing!"; | ||||||
|
||||||
pub fn reply_state_machine(message: &str) -> &str { | ||||||
let message = message.trim(); | ||||||
if message.is_empty() { | ||||||
return FINE_BE_THAT_WAY; | ||||||
} | ||||||
let mut state = State::Initial; | ||||||
for c in message.chars().rev() { | ||||||
match state { | ||||||
State::Initial => { | ||||||
state = if c == '?' { | ||||||
State::HasQuestionMark | ||||||
} else { | ||||||
if c.is_lowercase() { | ||||||
return WHATEVER; | ||||||
} | ||||||
if c.is_uppercase() { | ||||||
State::NoQuestionMarkUpperCase | ||||||
} else { | ||||||
State::NoQuestionMarkUndefined | ||||||
} | ||||||
}; | ||||||
} | ||||||
State::HasQuestionMark => { | ||||||
state = if c.is_uppercase() { | ||||||
State::QuestionMarkUpperCase | ||||||
} else { | ||||||
State::QuestionMarkUndefined | ||||||
}; | ||||||
} | ||||||
State::NoQuestionMarkUpperCase => { | ||||||
if c.is_lowercase() { | ||||||
return WHATEVER; | ||||||
} | ||||||
} | ||||||
State::NoQuestionMarkUndefined => { | ||||||
if c.is_lowercase() { | ||||||
return WHATEVER; | ||||||
} | ||||||
if c.is_uppercase() { | ||||||
state = State::NoQuestionMarkUpperCase; | ||||||
} | ||||||
} | ||||||
State::QuestionMarkUpperCase => { | ||||||
if c.is_lowercase() { | ||||||
return SURE; | ||||||
} | ||||||
} | ||||||
State::QuestionMarkUndefined => { | ||||||
if c.is_lowercase() { | ||||||
return SURE; | ||||||
} | ||||||
if c.is_uppercase() { | ||||||
state = State::QuestionMarkUpperCase; | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
match state { | ||||||
State::HasQuestionMark | State::QuestionMarkUndefined => SURE, | ||||||
State::NoQuestionMarkUpperCase => CHILL_OUT, | ||||||
State::NoQuestionMarkUndefined => WHATEVER, | ||||||
State::QuestionMarkUpperCase => CALM_DOWN, | ||||||
_ => panic!("Unexpected final state"), | ||||||
} | ||||||
} | ||||||
|
||||||
#[bench] | ||||||
/// multiple line question for match | ||||||
fn multiple_line_question_match(b: &mut Bencher) { | ||||||
b.iter(|| reply_match("\rDoes this cryogenic chamber make me look fat?\rNo.")); | ||||||
b.iter(|| { | ||||||
reply_match("\rDoes this cryogenic chamber make me look fat?\rNo."); | ||||||
reply_match(" "); | ||||||
reply_match("Does this cryogenic chamber make me look fat?"); | ||||||
reply_match("WHAT'S GOING ON?"); | ||||||
reply_match("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"); | ||||||
}); | ||||||
} | ||||||
|
||||||
#[bench] | ||||||
/// multiple line question for if statements | ||||||
fn multiple_line_question_if(b: &mut Bencher) { | ||||||
b.iter(|| reply_if_chain("\rDoes this cryogenic chamber make me look fat?\rNo.")); | ||||||
b.iter(|| { | ||||||
reply_if_chain("\rDoes this cryogenic chamber make me look fat?\rNo."); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The inputs to the functions under test should be wrapped in |
||||||
reply_if_chain(" "); | ||||||
reply_if_chain("Does this cryogenic chamber make me look fat?"); | ||||||
reply_if_chain("WHAT'S GOING ON?"); | ||||||
reply_if_chain("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"); | ||||||
}); | ||||||
} | ||||||
|
||||||
#[bench] | ||||||
/// multiple line question for answer array | ||||||
fn multiple_line_question_array(b: &mut Bencher) { | ||||||
b.iter(|| reply_array("\rDoes this cryogenic chamber make me look fat?\rNo.")); | ||||||
b.iter(|| { | ||||||
reply_array("\rDoes this cryogenic chamber make me look fat?\rNo."); | ||||||
reply_array(" "); | ||||||
reply_array("Does this cryogenic chamber make me look fat?"); | ||||||
reply_array("WHAT'S GOING ON?"); | ||||||
reply_array("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"); | ||||||
}); | ||||||
} | ||||||
|
||||||
#[bench] | ||||||
/// multiple line question for state machine | ||||||
fn multiple_line_question_state_machine(b: &mut Bencher) { | ||||||
b.iter(|| { | ||||||
reply_state_machine("\rDoes this cryogenic chamber make me look fat?\rNo."); | ||||||
reply_state_machine(" "); | ||||||
reply_state_machine("Does this cryogenic chamber make me look fat?"); | ||||||
reply_state_machine("WHAT'S GOING ON?"); | ||||||
reply_state_machine("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"); | ||||||
}); | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -7,19 +7,21 @@ The [approaches page][approaches] lists two idiomatic approaches to this exercis | |||||||
1. [Using `if` statements][approach-if]. | ||||||||
2. [Using `match` on a `tuple`][approach-match]. | ||||||||
|
||||||||
For our performance investigation, we'll also include a third approach that [uses an answer array][approach-answer-array]. | ||||||||
For our performance investigation, we'll also include a third approach that [uses an answer array][approach-answer-array] and | ||||||||
a fourth that uses a state machine. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Our convention in markdown is one sentence per line. |
||||||||
|
||||||||
## Benchmarks | ||||||||
|
||||||||
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application]. | ||||||||
|
||||||||
``` | ||||||||
test multiple_line_question_match ... bench: 96 ns/iter (+/- 17) | ||||||||
test multiple_line_question_if ... bench: 97 ns/iter (+/- 12) | ||||||||
test multiple_line_question_array ... bench: 100 ns/iter (+/- 2) | ||||||||
test multiple_line_question_array ... bench: 175.80 ns/iter (+/- 5.91) | ||||||||
test multiple_line_question_if ... bench: 157.26 ns/iter (+/- 8.69) | ||||||||
test multiple_line_question_match ... bench: 152.95 ns/iter (+/- 5.79) | ||||||||
test multiple_line_question_state_machine ... bench: 89.17 ns/iter (+/- 9.65) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cannot reproduce this result. On my machine, the state machine approach is consistently the slowest:
|
||||||||
``` | ||||||||
|
||||||||
All three approaches are close in performance, but the `if` statements and `match` approaches may be considered to be more idiomatic. | ||||||||
All four approaches are close in performance, but the `if` statements and `match` approaches may be considered to be more idiomatic. The state machine approach is the fastest because it loops through the characters of the message only once. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
same here, one sentence per line |
||||||||
|
||||||||
[approaches]: https://exercism.org/tracks/rust/exercises/bob/approaches | ||||||||
[approach-if]: https://exercism.org/tracks/rust/exercises/bob/approaches/if-statements | ||||||||
|
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.
A separate function could make sense here, to make it more clear that all benches are running the same code.