在日常交流中,我们会根据对方的话语内容和语气来决定如何回应。有些话语是问题,有些是喊叫,有些是沉默,还有些是普通的陈述。在 Exercism 的 "bob" 练习中,我们将实现一个模拟青少年鲍勃(Bob)的程序,他根据不同的输入给出不同的回应。这不仅能帮助我们掌握字符串处理技巧,还能深入学习如何用 Rust 实现状态机和复杂的条件逻辑。
鲍勃的回应规则
鲍勃是一个懒惰的青少年,他对不同类型的输入有不同的回应:
- 无声:如果输入是沉默或只有空白字符,鲍勃回答 "Fine. Be that way!"
- 喊叫:如果输入完全是大写字母(且包含至少一个字母),鲍勃回答 "Whoa, chill out!"
- 喊叫的问题:如果输入既是喊叫又是问题,鲍勃回答 "Calm down, I know what I'm doing!"
- 问题:如果输入以问号结尾,鲍勃回答 "Sure."
- 其他:对其他所有输入,鲍勃回答 "Whatever."
让我们先看看练习提供的函数签名:
rust
pub fn reply(message: &str) -> &str {
unimplemented!("have Bob reply to the incoming message: {}", message)
}
我们需要实现这个函数,根据输入消息返回相应的回应。
算法实现
1. 基础实现
rust
pub fn reply(message: &str) -> &str {
let msg = message.trim();
if msg.is_empty() {
return "Fine. Be that way!";
}
let is_question = msg.ends_with('?');
let is_yelling = msg.chars().any(|c| c.is_alphabetic())
&& msg == msg.to_uppercase();
match (is_yelling, is_question) {
(true, true) => "Calm down, I know what I'm doing!",
(true, false) => "Whoa, chill out!",
(false, true) => "Sure.",
(false, false) => "Whatever.",
}
}
这个实现的关键点:
- 使用
trim()去除首尾空白字符 - 通过
is_empty()检查是否为空 - 使用
ends_with('?')检查是否为问题 - 通过检查是否包含字母且等于大写形式来判断是否在喊叫
2. 优化实现
rust
pub fn reply(message: &str) -> &str {
let msg = message.trim();
if msg.is_empty() {
return "Fine. Be that way!";
}
let is_question = msg.ends_with('?');
let is_yelling = is_yelling(msg);
match (is_yelling, is_question) {
(true, true) => "Calm down, I know what I'm doing!",
(true, false) => "Whoa, chill out!",
(false, true) => "Sure.",
(false, false) => "Whatever.",
}
}
fn is_yelling(msg: &str) -> bool {
let has_letters = msg.chars().any(|c| c.is_alphabetic());
has_letters && msg == msg.to_uppercase()
}
将喊叫判断逻辑提取到独立函数中,提高代码可读性。
测试用例分析
通过查看测试用例,我们可以更好地理解需求:
rust
#[test]
/// stating something
fn test_stating_something() {
process_response_case("Tom-ay-to, tom-aaaah-to.", "Whatever.");
}
普通陈述返回 "Whatever."。
rust
#[test]
/// shouting numbers
fn test_shouting_numbers() {
process_response_case("1, 2, 3 GO!", "Whoa, chill out!");
}
包含字母的全大写被认为是喊叫。
rust
#[test]
/// forceful question
fn test_forceful_question() {
process_response_case("WHAT'S GOING ON?", "Calm down, I know what I'm doing!");
}
既是喊叫又是问题的输入返回特殊回应。
rust
#[test]
/// silence
fn test_silence() {
process_response_case("", "Fine. Be that way!");
}
空字符串被认为是沉默。
rust
#[test]
/// prolonged silence
fn test_prolonged_silence() {
process_response_case(" ", "Fine. Be that way!");
}
只有空白字符也被认为是沉默。
rust
#[test]
/// statement containing question mark
fn test_statement_containing_question_mark() {
process_response_case("Ending with ? means a question.", "Whatever.");
}
句中包含问号但不以问号结尾的不是问题。
完整实现
考虑所有边界情况的完整实现:
rust
pub fn reply(message: &str) -> &str {
let msg = message.trim();
if msg.is_empty() {
return "Fine. Be that way!";
}
let is_question = msg.ends_with('?');
let is_yelling = is_yelling(msg);
match (is_yelling, is_question) {
(true, true) => "Calm down, I know what I'm doing!",
(true, false) => "Whoa, chill out!",
(false, true) => "Sure.",
(false, false) => "Whatever.",
}
}
fn is_yelling(msg: &str) -> bool {
let has_letters = msg.chars().any(|c| c.is_alphabetic());
has_letters && msg == msg.to_uppercase() && msg != msg.to_lowercase()
}
在 is_yelling 函数中添加 msg != msg.to_lowercase() 条件,确保字符串中确实包含字母。
状态机实现
我们可以将鲍勃的逻辑看作一个状态机:
rust
#[derive(Debug, PartialEq)]
enum MessageState {
Silence,
YellingQuestion,
Yelling,
Question,
Statement,
}
pub fn reply(message: &str) -> &str {
let state = analyze_message(message);
match state {
MessageState::Silence => "Fine. Be that way!",
MessageState::YellingQuestion => "Calm down, I know what I'm doing!",
MessageState::Yelling => "Whoa, chill out!",
MessageState::Question => "Sure.",
MessageState::Statement => "Whatever.",
}
}
fn analyze_message(message: &str) -> MessageState {
let msg = message.trim();
if msg.is_empty() {
return MessageState::Silence;
}
let is_question = msg.ends_with('?');
let is_yelling = is_yelling(msg);
match (is_yelling, is_question) {
(true, true) => MessageState::YellingQuestion,
(true, false) => MessageState::Yelling,
(false, true) => MessageState::Question,
(false, false) => MessageState::Statement,
}
}
fn is_yelling(msg: &str) -> bool {
let has_letters = msg.chars().any(|c| c.is_alphabetic());
has_letters && msg == msg.to_uppercase() && msg != msg.to_lowercase()
}
这种状态机的方法使逻辑更加清晰。
使用模式匹配的实现
rust
pub fn reply(message: &str) -> &str {
let msg = message.trim();
if msg.is_empty() {
return "Fine. Be that way!";
}
let is_question = msg.ends_with('?');
let is_yelling = {
let has_letters = msg.chars().any(|c| c.is_alphabetic());
has_letters && msg == msg.to_uppercase() && msg != msg.to_lowercase()
};
match (is_yelling, is_question) {
(true, true) => "Calm down, I know what I'm doing!",
(true, false) => "Whoa, chill out!",
(false, true) => "Sure.",
(false, false) => "Whatever.",
}
}
错误处理和边界情况
考虑更多边界情况的实现:
rust
pub fn reply(message: &str) -> &str {
// 处理 Unicode 空白字符
let msg = message.trim_matches(|c: char| c.is_whitespace());
// 检查是否为空(包括只有空白字符的情况)
if msg.is_empty() {
return "Fine. Be that way!";
}
// 检查是否为问题(以问号结尾)
let is_question = msg.ends_with('?');
// 检查是否在喊叫
let is_yelling = {
// 必须包含至少一个字母
let has_letters = msg.chars().any(|c| c.is_alphabetic());
// 必须全部是大写且不全为小写
has_letters && msg == msg.to_uppercase() && msg != msg.to_lowercase()
};
// 根据状态返回相应回应
match (is_yelling, is_question) {
(true, true) => "Calm down, I know what I'm doing!",
(true, false) => "Whoa, chill out!",
(false, true) => "Sure.",
(false, false) => "Whatever.",
}
}
扩展功能
基于基础实现,我们可以添加更多功能:
rust
pub struct Bob;
impl Bob {
pub fn reply(message: &str) -> &str {
Self::analyze_message(message)
}
fn analyze_message(message: &str) -> &str {
let msg = message.trim_matches(|c: char| c.is_whitespace());
if msg.is_empty() {
return Self::silence_response();
}
let is_question = Self::is_question(msg);
let is_yelling = Self::is_yelling(msg);
match (is_yelling, is_question) {
(true, true) => Self::yelling_question_response(),
(true, false) => Self::yelling_response(),
(false, true) => Self::question_response(),
(false, false) => Self::statement_response(),
}
}
fn is_question(msg: &str) -> bool {
msg.ends_with('?')
}
fn is_yelling(msg: &str) -> bool {
let has_letters = msg.chars().any(|c| c.is_alphabetic());
has_letters && msg == msg.to_uppercase() && msg != msg.to_lowercase()
}
fn silence_response() -> &'static str {
"Fine. Be that way!"
}
fn yelling_question_response() -> &'static str {
"Calm down, I know what I'm doing!"
}
fn yelling_response() -> &'static str {
"Whoa, chill out!"
}
fn question_response() -> &'static str {
"Sure."
}
fn statement_response() -> &'static str {
"Whatever."
}
}
性能优化
考虑性能的优化实现:
rust
pub fn reply(message: &str) -> &str {
// 使用字节检查优化空白字符处理
let msg = message.trim_start_matches(|c: char| c.is_whitespace())
.trim_end_matches(|c: char| c.is_whitespace());
// 快速检查空消息
if msg.is_empty() {
return "Fine. Be that way!";
}
// 单次遍历确定消息属性
let (is_question, is_yelling) = analyze_properties(msg);
match (is_yelling, is_question) {
(true, true) => "Calm down, I know what I'm doing!",
(true, false) => "Whoa, chill out!",
(false, true) => "Sure.",
(false, false) => "Whatever.",
}
}
fn analyze_properties(msg: &str) -> (bool, bool) {
let mut has_letters = false;
let mut all_uppercase = true;
let is_question = msg.ends_with('?');
for c in msg.chars() {
if c.is_alphabetic() {
has_letters = true;
if c.is_lowercase() {
all_uppercase = false;
}
} else if !c.is_whitespace() && !c.is_ascii_punctuation() {
// 非字母字符不影响大小写判断
continue;
}
}
let is_yelling = has_letters && all_uppercase;
(is_question, is_yelling)
}
实际应用场景
虽然鲍勃看起来只是一个练习,但它在实际开发中有以下应用:
- 聊天机器人:根据用户输入的语气和内容给出不同回应
- 情感分析:分析文本的情感倾向
- 自然语言处理:理解用户意图和上下文
- 用户界面:根据用户输入提供智能反馈
与其他实现方式的比较
rust
// 使用正则表达式的实现
use regex::Regex;
pub fn reply_regex(message: &str) -> &str {
let msg = message.trim();
if msg.is_empty() {
return "Fine. Be that way!";
}
let yelling_re = Regex::new(r"[A-Z]").unwrap();
let letter_re = Regex::new(r"[a-zA-Z]").unwrap();
let is_question = msg.ends_with('?');
let is_yelling = letter_re.is_match(msg)
&& !msg.chars().any(|c| c.is_alphabetic() && c.is_lowercase());
match (is_yelling, is_question) {
(true, true) => "Calm down, I know what I'm doing!",
(true, false) => "Whoa, chill out!",
(false, true) => "Sure.",
(false, false) => "Whatever.",
}
}
// 使用函数式编程风格的实现
pub fn reply_functional(message: &str) -> &str {
let msg = message.trim();
if msg.is_empty() {
return "Fine. Be that way!";
}
let properties = msg.chars().fold(
(false, false, msg.ends_with('?')),
|(has_letters, all_upper, is_question), c| {
if c.is_alphabetic() {
(true, all_upper && !c.is_lowercase(), is_question)
} else {
(has_letters, all_upper, is_question)
}
}
);
let (has_letters, all_upper, is_question) = properties;
let is_yelling = has_letters && all_upper;
match (is_yelling, is_question) {
(true, true) => "Calm down, I know what I'm doing!",
(true, false) => "Whoa, chill out!",
(false, true) => "Sure.",
(false, false) => "Whatever.",
}
}
总结
通过 bob 练习,我们学到了:
- 字符串处理:掌握了 Rust 中字符串的处理技巧
- 状态机思维:学会了如何将复杂逻辑建模为状态机
- 条件逻辑:熟练使用模式匹配处理复杂的条件分支
- 边界处理:理解了如何处理各种边界情况
- 性能优化:了解了字符串处理的优化技巧
- 代码组织:学会了如何组织和重构复杂逻辑
这些技能在实际开发中非常有用,特别是在处理用户输入、实现聊天机器人、进行文本分析和构建智能界面时。鲍勃练习虽然看起来简单,但它涉及到了字符串处理、条件逻辑和状态判断等许多核心概念,是学习 Rust 文本处理的良好起点。
通过这个练习,我们也看到了 Rust 在处理字符串和实现复杂逻辑方面的强大能力,以及如何用清晰且高效的代码表达复杂的业务规则。这种结合了安全性和性能的语言特性正是 Rust 的魅力所在。