Rust 练习册 60:鲍勃与字符串处理的状态机思维

在日常交流中,我们会根据对方的话语内容和语气来决定如何回应。有些话语是问题,有些是喊叫,有些是沉默,还有些是普通的陈述。在 Exercism 的 "bob" 练习中,我们将实现一个模拟青少年鲍勃(Bob)的程序,他根据不同的输入给出不同的回应。这不仅能帮助我们掌握字符串处理技巧,还能深入学习如何用 Rust 实现状态机和复杂的条件逻辑。

鲍勃的回应规则

鲍勃是一个懒惰的青少年,他对不同类型的输入有不同的回应:

  1. 无声:如果输入是沉默或只有空白字符,鲍勃回答 "Fine. Be that way!"
  2. 喊叫:如果输入完全是大写字母(且包含至少一个字母),鲍勃回答 "Whoa, chill out!"
  3. 喊叫的问题:如果输入既是喊叫又是问题,鲍勃回答 "Calm down, I know what I'm doing!"
  4. 问题:如果输入以问号结尾,鲍勃回答 "Sure."
  5. 其他:对其他所有输入,鲍勃回答 "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)
}

实际应用场景

虽然鲍勃看起来只是一个练习,但它在实际开发中有以下应用:

  1. 聊天机器人:根据用户输入的语气和内容给出不同回应
  2. 情感分析:分析文本的情感倾向
  3. 自然语言处理:理解用户意图和上下文
  4. 用户界面:根据用户输入提供智能反馈

与其他实现方式的比较

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 练习,我们学到了:

  1. 字符串处理:掌握了 Rust 中字符串的处理技巧
  2. 状态机思维:学会了如何将复杂逻辑建模为状态机
  3. 条件逻辑:熟练使用模式匹配处理复杂的条件分支
  4. 边界处理:理解了如何处理各种边界情况
  5. 性能优化:了解了字符串处理的优化技巧
  6. 代码组织:学会了如何组织和重构复杂逻辑

这些技能在实际开发中非常有用,特别是在处理用户输入、实现聊天机器人、进行文本分析和构建智能界面时。鲍勃练习虽然看起来简单,但它涉及到了字符串处理、条件逻辑和状态判断等许多核心概念,是学习 Rust 文本处理的良好起点。

通过这个练习,我们也看到了 Rust 在处理字符串和实现复杂逻辑方面的强大能力,以及如何用清晰且高效的代码表达复杂的业务规则。这种结合了安全性和性能的语言特性正是 Rust 的魅力所在。

相关推荐
淡淡蓝蓝2 小时前
uni.uploadFile使用PUT方法上传图片
开发语言·前端·javascript
PyHaVolask2 小时前
PHP基础入门
开发语言·php
BINGCHN2 小时前
流量分析入门(二):wireshark的使用
网络·测试工具·wireshark
桃花岛主702 小时前
multipart/form-data 和 application/x-www-form-urlencoded区别
服务器·网络·网络协议·http
乘乘凉2 小时前
C#中的值传递和引用传递
java·开发语言·c#
ThreeYear_s2 小时前
【FPGA+DSP系列】——MATLAB simulink单相PWM全控整流电路基础版
开发语言·matlab·fpga开发
kaikaile19952 小时前
基于高斯白噪声与瑞利衰落共同作用的OFDM系统仿真
开发语言·matlab
s9123601012 小时前
【Openwrt】M4 Macmini编译Openwrt的Dockerfile
rust
zwm_yy3 小时前
php常用函数
开发语言·php·restful