mirror of
https://codeberg.org/JasterV/aoc2024-haskell.git
synced 2026-04-26 18:10:05 +00:00
refactor: day 1 to handle errors properly
This commit is contained in:
parent
4a32ec5bbd
commit
27986bed3b
1 changed files with 67 additions and 33 deletions
100
src/Day1.hs
100
src/Day1.hs
|
|
@ -1,43 +1,77 @@
|
|||
module Day1 (
|
||||
partOne,
|
||||
partTwo,
|
||||
) where
|
||||
module Day1
|
||||
( partOne,
|
||||
partTwo,
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Exception
|
||||
import Data.Data
|
||||
import qualified Data.Bifunctor as Bifunctor
|
||||
import Data.Either
|
||||
import Data.List (sort)
|
||||
import Text.Read (readEither)
|
||||
import Prelude hiding (id)
|
||||
|
||||
type LocationID = Int
|
||||
|
||||
data ParseError = ParseError
|
||||
deriving (Show, Typeable)
|
||||
data Error = ParseLocationError String String | ParseLineError String
|
||||
deriving (Show)
|
||||
|
||||
instance Exception ParseError
|
||||
partOne :: String -> IO (Either Error Int)
|
||||
partOne filepath = do
|
||||
contents <- readFile filepath
|
||||
let score = calculateScore <$> parseInput contents
|
||||
return score
|
||||
where
|
||||
-- To calculate the overall score we just need to
|
||||
-- sort the lists and calculate the distances of each element
|
||||
calculateScore (left, right) =
|
||||
sum $ zipWith distance (sort left) (sort right)
|
||||
distance a b = abs (a - b)
|
||||
|
||||
partOne :: IO Int
|
||||
partOne = do
|
||||
contents <- readFile "input/day1.txt"
|
||||
let (left, right) = parseInput contents
|
||||
let score = sum $ zipWith distance (sort left) (sort right)
|
||||
pure score
|
||||
where
|
||||
distance a b = abs (a - b)
|
||||
partTwo :: String -> IO (Either Error Int)
|
||||
partTwo filepath = do
|
||||
contents <- readFile filepath
|
||||
let score = calculateScore <$> parseInput contents
|
||||
return score
|
||||
where
|
||||
-- To calculate the overall score we need to sum all the similarity scores
|
||||
-- of each element from the left list applied to the right list
|
||||
calculateScore (left, right) = sum $ map (`similarity` right) left
|
||||
-- The similarity score of a location id is equal to itself
|
||||
-- multiplied by the number of times it appears on the list
|
||||
similarity id ids = id * count id ids
|
||||
count x = length . filter (== x)
|
||||
|
||||
partTwo :: IO Int
|
||||
partTwo = do
|
||||
contents <- readFile "input/day1.txt"
|
||||
let (left, right) = parseInput contents
|
||||
let score = sum $ map (`similarity` right) left
|
||||
pure score
|
||||
where
|
||||
similarity :: LocationID -> [LocationID] -> Int
|
||||
similarity x xs = x * count x xs
|
||||
parseInput :: String -> Either Error ([LocationID], [LocationID])
|
||||
parseInput input = do
|
||||
parsedLines <- parseLines (lines input)
|
||||
return (unzip parsedLines)
|
||||
|
||||
count :: LocationID -> [LocationID] -> Int
|
||||
count x = length . filter (== x)
|
||||
parseLines :: [String] -> Either Error [(LocationID, LocationID)]
|
||||
parseLines xs = case take 1 errors of
|
||||
[parseError] -> Left parseError
|
||||
_ -> Right parsedLines
|
||||
where
|
||||
errors = lefts $ map (parseWords . words) xs
|
||||
parsedLines = rights $ map (parseWords . words) xs
|
||||
|
||||
parseInput :: String -> ([LocationID], [LocationID])
|
||||
parseInput = unzip . map (parseWords . words) . lines
|
||||
where
|
||||
parseWords [left, right] = (read left :: LocationID, read right :: LocationID)
|
||||
parseWords _ = throw ParseError
|
||||
parseWords :: [String] -> Either Error (LocationID, LocationID)
|
||||
parseWords [left, right] = do
|
||||
leftLocation <- parseLeft
|
||||
rightLocation <- parseRight
|
||||
return (leftLocation, rightLocation)
|
||||
where
|
||||
parseLeft :: Either Error LocationID
|
||||
parseLeft =
|
||||
Bifunctor.first
|
||||
(ParseLocationError left)
|
||||
(readEither left :: Either String LocationID)
|
||||
|
||||
parseRight :: Either Error LocationID
|
||||
parseRight =
|
||||
Bifunctor.first
|
||||
(ParseLocationError right)
|
||||
(readEither right :: Either String LocationID)
|
||||
|
||||
-- If the line does not contain exactly two words,
|
||||
-- it is considered an error
|
||||
parseWords xs' = Left $ ParseLineError (unwords xs')
|
||||
|
|
|
|||
Loading…
Reference in a new issue