patternrustMinor
Advent of Code 2016: Day 1, Part 1
Viewed 0 times
adventpartcodeday2016
Problem
I'm working through Advent of Code 2016 in Rust. The prizes go to the swift, but I'm aiming for well-written code. This is Day 1.
To summarize the problem statement, given a instruction string of turns and distances (e.g. "R5, L5, R5, R3"), return the Manhattan distance from the origin (in this case 12).
I am using Rust version
lib.rs
day_01.rs
```
use std::convert::TryFrom;
pub fn blocks_away(instructions: &str) -> i16 {
let mut x = 0;
let mut y = 0;
let mut direction = Direction::North;
for instruction in Instruction::try_many_from(instructions).unwrap() {
direction.turn(instruction.turn);
match dire
To summarize the problem statement, given a instruction string of turns and distances (e.g. "R5, L5, R5, R3"), return the Manhattan distance from the origin (in this case 12).
I am using Rust version
1.15.0-nightly (71c06a56a 2016-12-18).lib.rs
#![feature(try_from)]
#![feature(more_struct_aliases)]
mod day_01;
pub fn day_01() {
let day_01_answer =
day_01::blocks_away("L4, L1, R4, R1, R1, L3, R5, L5, L2, L3, R2, R1, L4, R5, R4, L2, R1, \
R3, L5, R1, L3, L2, R5, L4, L5, R1, R2, L1, R5, L3, R2, R2, L1, R5, \
R2, L1, L1, R2, L1, R1, L2, L2, R4, R3, R2, L3, L188, L3, R2, R54, \
R1, R1, L2, L4, L3, L2, R3, L1, L1, R3, R5, L1, R5, L1, L1, R2, R4, \
R4, L5, L4, L1, R2, R4, R5, L2, L3, R5, L5, R1, R5, L2, R4, L2, L1, \
R4, R3, R4, L4, R3, L4, R78, R2, L3, R188, R2, R3, L2, R2, R3, R1, \
R5, R1, L1, L1, R4, R2, R1, R5, L1, R4, L4, R2, R5, L2, L5, R4, L3, \
L2, R1, R1, L5, L4, R1, L5, L1, L5, L1, L4, L3, L5, R4, R5, R2, L5, \
R5, R5, R4, R2, L1, L2, R3, R5, R5, R5, L2, L1, R4, R3, R1, L4, L2, \
L3, R2, L3, L5, L2, L2, L1, L2, R5, L2, L2, L3, L1, R1, L4, R2, L4, \
R3, R5, R3, R4, R1, R5, L3, L5, L5, L3, L2, L1, R3, L4, R3, R2, L1, \
R3, R1, L2, R4, L3, L3, L3, L1, L2");
assert_eq!(day_01_answer, 279);
}day_01.rs
```
use std::convert::TryFrom;
pub fn blocks_away(instructions: &str) -> i16 {
let mut x = 0;
let mut y = 0;
let mut direction = Direction::North;
for instruction in Instruction::try_many_from(instructions).unwrap() {
direction.turn(instruction.turn);
match dire
Solution
day_01.rs
``
use std::str::FromStr;
type Error = &'static str;
type Result = ::std::result::Result;
pub fn blocks_away(instructions: &str) -> Result {
let mut x = 0;
let mut y = 0;
let mut direction = Direction::North;
for instruction in Instruction::try_many_from(instructions) {
let instruction = instruction?;
direction.turn(instruction.turn);
match direction {
Direction::North => y += instruction.blocks,
Direction::East => x += instruction.blocks,
Direction::South => y -= instruction.blocks,
Direction::West => x -= instruction.blocks,
}
}
Ok(x.abs() + y.abs())
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Direction {
North,
East,
South,
West,
}
impl Direction {
fn turn(&mut self, turn: Turn) {
use self::Direction::*;
use self::Turn::*;
self = match (self, turn) {
(North, Left) => West,
(North, Right) => East,
(East, Left) => North,
(East, Right) => South,
(South, Left) => East,
(South, Right) => West,
(West, Left) => South,
(West, Right) => North,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Turn {
Left,
Right,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Instruction {
turn: Turn,
blocks: i16,
}
impl Instruction {
fn try_many_from(s: &'a str) -> impl Iterator> + 'a {
s.split(", ").map(str::parse)
}
}
impl FromStr for Instruction {
type Err = Error;
fn from_str(s: &str) -> Result {
let mut chars = s.chars();
let turn = match chars.next() {
Some('L') => Turn::Left,
Some('R') => Turn::Right,
Some(_) => return Err("Turn character invalid"),
None => return Err("Instruction string is empty"),
};
let blocks = try!(chars.as_str().parse().map_err(|_| "Could not parse blocks"));
Ok(Instruction {
turn: turn,
blocks: blocks,
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_instruction_success() {
let instruction = "L1".parse::();
assert_eq!(instruction,
Ok(Instruction {
turn: Turn::Left,
blocks: 1,
}));
let instruction = "R2".parse::();
assert_eq!(instruction,
Ok(Instruction {
turn: Turn::Right,
blocks: 2,
}));
}
#[test]
fn test_parse_instruction_invalid_turn_character() {
let instruction = "S1".parse::();
assert_eq!(instruction, Err("Turn character invalid"));
}
#[test]
fn test_parse_instruction_empty_string() {
let instruction = "".parse::();
assert_eq!(instruction, Err("Instruction string is empty"));
}
#[test]
fn test_parse_instruction_missing_blocks_digit() {
let instruction = "L".parse::();
assert_eq!(instruction, Err("Could not parse blocks"));
}
#[test]
fn test_parse_instruction_invalid_blocks_digit() {
let instruction = "LL".parse::();
assert_eq!(instruction, Err("Could not parse blocks"));
}
#[test]
fn test_parse_instructions() {
let instructions = Instruction::try_many_from("L1, R2").collect::>>();
assert_eq!(instructions,
Ok(vec![Instruction {
turn: Turn::Left,
blocks: 1,
},
Instruction {
turn: Turn::Right,
blocks: 2,
- I Would return an error from
blocks_awayas it's based on input to the function, thus outside of the functions control.
- The error and result types are repeated, make type aliases to DRY that up.
- Make enums
CopyandClone.
- Collapse
turninto a single match on a tuple.
- Import the enum variants in
turnto decrease clutter.
- I might just return the new direction instead of modifying
self. This is a bit more flexible.
- Why is blocks a signed integer?
- Collecting to a
Vecis unneeded allocation. I'd inline themapor use new feature:conservative_impl_trait!
- Should not have `
onimplblock forInstruction!Instructionhas no generic lifetime parameters and it's not a shorthand syntax.
- TryFrom
already exists:FromStr.
- Your error type is a 'static
string, not tied to the input string lifetime.
- Use Self::Err
to avoid repeating in the parsing code return (or the localResult)
- Collapse the nested matches in string parsing.
- The turbofish when parsing for blocks
is unneeded, the type can be inferred.
- I prefer avoiding unwrap
whenever possible. You canassert_eq!againstOk(...)instead of unwrapping.
``
use std::str::FromStr;
type Error = &'static str;
type Result = ::std::result::Result;
pub fn blocks_away(instructions: &str) -> Result {
let mut x = 0;
let mut y = 0;
let mut direction = Direction::North;
for instruction in Instruction::try_many_from(instructions) {
let instruction = instruction?;
direction.turn(instruction.turn);
match direction {
Direction::North => y += instruction.blocks,
Direction::East => x += instruction.blocks,
Direction::South => y -= instruction.blocks,
Direction::West => x -= instruction.blocks,
}
}
Ok(x.abs() + y.abs())
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Direction {
North,
East,
South,
West,
}
impl Direction {
fn turn(&mut self, turn: Turn) {
use self::Direction::*;
use self::Turn::*;
self = match (self, turn) {
(North, Left) => West,
(North, Right) => East,
(East, Left) => North,
(East, Right) => South,
(South, Left) => East,
(South, Right) => West,
(West, Left) => South,
(West, Right) => North,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Turn {
Left,
Right,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Instruction {
turn: Turn,
blocks: i16,
}
impl Instruction {
fn try_many_from(s: &'a str) -> impl Iterator> + 'a {
s.split(", ").map(str::parse)
}
}
impl FromStr for Instruction {
type Err = Error;
fn from_str(s: &str) -> Result {
let mut chars = s.chars();
let turn = match chars.next() {
Some('L') => Turn::Left,
Some('R') => Turn::Right,
Some(_) => return Err("Turn character invalid"),
None => return Err("Instruction string is empty"),
};
let blocks = try!(chars.as_str().parse().map_err(|_| "Could not parse blocks"));
Ok(Instruction {
turn: turn,
blocks: blocks,
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_instruction_success() {
let instruction = "L1".parse::();
assert_eq!(instruction,
Ok(Instruction {
turn: Turn::Left,
blocks: 1,
}));
let instruction = "R2".parse::();
assert_eq!(instruction,
Ok(Instruction {
turn: Turn::Right,
blocks: 2,
}));
}
#[test]
fn test_parse_instruction_invalid_turn_character() {
let instruction = "S1".parse::();
assert_eq!(instruction, Err("Turn character invalid"));
}
#[test]
fn test_parse_instruction_empty_string() {
let instruction = "".parse::();
assert_eq!(instruction, Err("Instruction string is empty"));
}
#[test]
fn test_parse_instruction_missing_blocks_digit() {
let instruction = "L".parse::();
assert_eq!(instruction, Err("Could not parse blocks"));
}
#[test]
fn test_parse_instruction_invalid_blocks_digit() {
let instruction = "LL".parse::();
assert_eq!(instruction, Err("Could not parse blocks"));
}
#[test]
fn test_parse_instructions() {
let instructions = Instruction::try_many_from("L1, R2").collect::>>();
assert_eq!(instructions,
Ok(vec![Instruction {
turn: Turn::Left,
blocks: 1,
},
Instruction {
turn: Turn::Right,
blocks: 2,
Code Snippets
use std::str::FromStr;
type Error = &'static str;
type Result<T> = ::std::result::Result<T, Error>;
pub fn blocks_away(instructions: &str) -> Result<i16> {
let mut x = 0;
let mut y = 0;
let mut direction = Direction::North;
for instruction in Instruction::try_many_from(instructions) {
let instruction = instruction?;
direction.turn(instruction.turn);
match direction {
Direction::North => y += instruction.blocks,
Direction::East => x += instruction.blocks,
Direction::South => y -= instruction.blocks,
Direction::West => x -= instruction.blocks,
}
}
Ok(x.abs() + y.abs())
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Direction {
North,
East,
South,
West,
}
impl Direction {
fn turn(&mut self, turn: Turn) {
use self::Direction::*;
use self::Turn::*;
*self = match (*self, turn) {
(North, Left) => West,
(North, Right) => East,
(East, Left) => North,
(East, Right) => South,
(South, Left) => East,
(South, Right) => West,
(West, Left) => South,
(West, Right) => North,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Turn {
Left,
Right,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Instruction {
turn: Turn,
blocks: i16,
}
impl Instruction {
fn try_many_from<'a>(s: &'a str) -> impl Iterator<Item = Result<Self>> + 'a {
s.split(", ").map(str::parse)
}
}
impl FromStr for Instruction {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut chars = s.chars();
let turn = match chars.next() {
Some('L') => Turn::Left,
Some('R') => Turn::Right,
Some(_) => return Err("Turn character invalid"),
None => return Err("Instruction string is empty"),
};
let blocks = try!(chars.as_str().parse().map_err(|_| "Could not parse blocks"));
Ok(Instruction {
turn: turn,
blocks: blocks,
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_instruction_success() {
let instruction = "L1".parse::<Instruction>();
assert_eq!(instruction,
Ok(Instruction {
turn: Turn::Left,
blocks: 1,
}));
let instruction = "R2".parse::<Instruction>();
assert_eq!(instruction,
Ok(Instruction {
turn: Turn::Right,
blocks: 2,
}));
}
#[test]
fn test_parse_instruction_invalid_turn_character() {
let instruction = "S1".parse::<Instruction>();
assert_eq!(instruction, Err("Turn character invalid"));
}
#[test]
fn test_parse_instruction_empty_string() {
let instruction = "".parse::#![feature(conservative_impl_trait)]
mod day_01;
pub fn day_01() {
let day_01_answer =
day_01::blocks_away("L4, L1, R4, R1, R1, L3, R5, L5, L2, L3, R2, R1, L4, R5, R4, L2, R1, \
R3, L5, R1, L3, L2, R5, L4, L5, R1, R2, L1, R5, L3, R2, R2, L1, R5, \
R2, L1, L1, R2, L1, R1, L2, L2, R4, R3, R2, L3, L188, L3, R2, R54, \
R1, R1, L2, L4, L3, L2, R3, L1, L1, R3, R5, L1, R5, L1, L1, R2, R4, \
R4, L5, L4, L1, R2, R4, R5, L2, L3, R5, L5, R1, R5, L2, R4, L2, L1, \
R4, R3, R4, L4, R3, L4, R78, R2, L3, R188, R2, R3, L2, R2, R3, R1, \
R5, R1, L1, L1, R4, R2, R1, R5, L1, R4, L4, R2, R5, L2, L5, R4, L3, \
L2, R1, R1, L5, L4, R1, L5, L1, L5, L1, L4, L3, L5, R4, R5, R2, L5, \
R5, R5, R4, R2, L1, L2, R3, R5, R5, R5, L2, L1, R4, R3, R1, L4, L2, \
L3, R2, L3, L5, L2, L2, L1, L2, R5, L2, L2, L3, L1, R1, L4, R2, L4, \
R3, R5, R3, R4, R1, R5, L3, L5, L5, L3, L2, L1, R3, L4, R3, R2, L1, \
R3, R1, L2, R4, L3, L3, L3, L1, L2");
assert_eq!(day_01_answer, Ok(279));
}Context
StackExchange Code Review Q#151121, answer score: 3
Revisions (0)
No revisions yet.