Hi,大家好,这里是炼手Rust专栏,我是xiaoK,今天来看个rust语言编程挑战:机器人模拟器。
提问
阿峰需要编写一个机器人模拟器。
机器人工厂的测试设施需要一个程序来验证机器人的运动。机器人有三种可能的运动:
右转
左转
前进
机器人被放置在一个假设的无限网格上,在一组 {x,y} 坐标处面向特定方向(北、东、南或西),例如坐标 {3,8},当面向北和东时,前进时坐标递增(可以想象一个平面坐标系,X 轴和 Y 轴的方向分别指向东和北)。
然后,机器人会收到许多指令,此时测试设备会验证机器人的新位置及其指向的方向。
例:
字母串“RAALAL”的含义是:
右转
提前两次
左转
前进一次
再次左转
假设机器人从 {7, 3} 开始,面向北。然后运行该指令流应使其位于面向西的 {9, 4}。
模板:
#[derive(PartialEq, Eq, Debug)]
pub enum Direction {
North,
East,
South,
West,
}
pub struct Robot;
impl Robot {
pub fn new(x: i32, y: i32, d: Direction) -> Self {
todo!("Create a robot at (x, y) ({x}, {y}) facing {d:?}")
}
#[must_use]
pub fn turn_right(self) -> Self {
todo!()
}
#[must_use]
pub fn turn_left(self) -> Self {
todo!()
}
#[must_use]
pub fn advance(self) -> Self {
todo!()
}
#[must_use]
pub fn instructions(self, instructions: &str) -> Self {
todo!("Follow the given sequence of instructions: {instructions}")
}
pub fn position(&self) -> (i32, i32) {
todo!()
}
pub fn direction(&self) -> &Direction {
todo!()
}
}
分析
观察模板,我们需要实现 7 个方法:
new方法需要创建一个Robot对象。turn_right变更方向即可,并且获得原来对象的所有权,返回一个新的对象。turn_left与turn_right类似。advance针对当前的方向,在x或者y上,加 1 或者减 1 。instructions方法,遍历当前指令的字符,针对每个字符,调用上述方法即可,很简单。position方法返回当前对象的坐标。direction方法返回当前对象的方向引用。
在简单的分析完成之后,我们就可以给出解决方案了。
解决方案
#[derive(PartialEq, Eq, Debug)]
pub enum Direction {
North,
East,
South,
West,
}
pub struct Robot {
x: i32,
y: i32,
d: Direction,
}
impl Robot {
pub fn new(x: i32, y: i32, d: Direction) -> Self {
Self { x, y, d }
}
pub fn turn_right(self) -> Self {
match self.d {
Direction::North => Self { d: Direction::East, ..self },
Direction::East => { Self { d: Direction::South, ..self } }
Direction::South => { Self { d: Direction::West, ..self } }
Direction::West => { Self { d: Direction::North, ..self } }
}
}
pub fn turn_left(self) -> Self {
match self.d {
Direction::North => Self { d: Direction::West, ..self },
Direction::East => { Self { d: Direction::North, ..self } }
Direction::South => { Self { d: Direction::East, ..self } }
Direction::West => { Self { d: Direction::South, ..self } }
}
}
pub fn advance(self) -> Self {
match self.d {
Direction::North => Self { y: self.y + 1, ..self },
Direction::East => { Self { x: self.x + 1, ..self } }
Direction::South => { Self { y: self.y - 1, ..self } }
Direction::West => { Self { x: self.x - 1, ..self } }
}
}
pub fn instructions(self, instructions: &str) -> Self {
let mut robot = self;
for x in instructions.chars() {
match x {
'L' => { robot = robot.turn_left() }
'R' => { robot = robot.turn_right() }
'A' => { robot = robot.advance() }
_ => {}
}
}
robot
}
pub fn position(&self) -> (i32, i32) {
(self.x, self.y)
}
pub fn direction(&self) -> &Direction {
&self.d
}
}
Trick
- 使用
match表达式,判断枚举分支。 - 结构体有个非常好用的复制方式,叫结构体更新语法。例如
Self { x: self.x + 1, ..self }。此处只更新了x的值,其他值与原来保持一致。 - 注意区分借用和移动语法,此模板的方法中:
new、turn_right、turn_left、advance、instructions使用了移动的语法,调用这些语法,会消耗掉原来的对象,原来的对象将不再可用。