首页 > 解决方案 > 解析一串单字符命令的有效方法是什么,每个命令可选地后跟一个整数重复计数?

问题描述

我正在实现一个机器人,它接受像L(左转)、R(右转)和M(前进)这样的命令。这些命令可以增加一个量词,例如M3LMR2(移动 3 步,左转,移动 1 步,转脸)。这相当于MMMLMRR.

我编写了可以理解以下枚举的机器人结构:

pub enum Message {                                                                                                                                                            
    TurnLeft(i8),                                                                                                                                                             
    TurnRight(i8),                                                                                                                                                            
    MoveForward(i8),                                                                                                                                                          
}

Robot::execute(&mut self, orders: Vec<Message>)正在正确地完成其工作。

现在,我正在努力为字符串解析、处理 、 和不安全切片编写一些体面的东西,&str因为String标记char可以是 1 个或多个字符。

我已经尝试过正则表达式匹配(几乎成功),但我真的想标记字符串:

fn capture(orders: &String, start: &usize, end: &usize) -> Message {
    unsafe {
        let order = orders.get_unchecked(start..end);
        // …
    };

    Message::TurnLeft(1) // temporary
}

pub fn parse_orders(orders: String) -> Result<Vec<Message>, String> {
    let mut messages = vec![];
    let mut start: usize = 0;
    let mut end: usize = 0;

    while end < orders.len() && end != start {
        end += 1;

        match orders.get(end) {
            Some('0'...'9') => continue,
            _ => {
                messages.push(capture(&orders, &start, &end));
                start = end;
            }
        }
    }

    Ok(messages)
}

这不会编译并且很笨拙。

这个想法是编写一个解析器,将订单字符串转换为 的向量Message

let messages = parse_order("M3LMR2");
println!("Messages => {:?}", messages);
// would print
// [Message::MoveForward(3), Message::TurnLeft(1), Message::MoveForward(1), Message::TurnRight(2)]

这样做的有效/优雅方式是什么?

标签: rust

解决方案


您可以使用迭代器、使用parse和一些基本String处理非常简单地做到这一点:

#[derive(Debug, PartialEq, Clone)]
enum Message {                                                                                                                                                            
    TurnLeft(u8),                                                                                                                                                             
    TurnRight(u8),                                                                                                                                                            
    MoveForward(u8),                                                                                                                                                          
}

struct RobotOrders(String);

impl RobotOrders {
    fn new(source: impl Into<String>) -> Self {
        RobotOrders(source.into())
    }
}

impl Iterator for RobotOrders {
    type Item = Message;

    fn next(&mut self) -> Option<Message> {
        self.0.chars().next()?;
        let order = self.0.remove(0);
        let n_digits = self.0.chars().take_while(char::is_ascii_digit).count();
        let mut number = self.0.clone();
        self.0 = number.split_off(n_digits);
        let number = number.parse().unwrap_or(1);

        Some(match order {
            'L' => Message::TurnLeft(number),
            'R' => Message::TurnRight(number),
            'M' => Message::MoveForward(number),
            _ => unimplemented!(),
        })
    }
}

fn main() {
    use Message::*;
    let orders = RobotOrders::new("M3LMR2");
    let should_be = [MoveForward(3), TurnLeft(1), MoveForward(1), TurnRight(2)];

    assert!(orders.eq(should_be.iter().cloned()));
}

推荐阅读