You are reading a single comment by @zooeyzooey and its replies. Click here to read the full conversation.
  • For day 1, there's quite an elegant solution using zipWith. Note that you don't actually have to chunk things up, as pointed out, since the windows overlap you only ever have to care about the start and end points (which can be achieved by zipping together the input list and the input list offset by the window length):

    module Main where
    
    inp :: String -> IO [Int]
    inp path = do
      c <- readFile path
      return $ map read (lines c)
    
    solve :: Int -> [Int] -> Int
    solve n xs = length . filter id $ zipWith (<) xs (drop n xs)
    
    main :: IO ()
    main = do
      vals <- inp "day01.input"
      print $ solve 1 vals
      print $ solve 3 vals
    

    FWIW, an idiomatic way of chunking a list would be:

    import Data.List (tails)
    
    chunk :: Int -> [a] -> [[a]]
    chunk n xs = foldr (zipWith (:)) (repeat []) (take n (tails xs))
    

    Why does this work? Suppose you have two lists [1, 2, 3, 4] and [2, 3, 4]. If you zip them together you get [(1, 2), (2, 3), (3, 4)] which is chunking by two. tails produces all tails of a list tails [1, 2, 3, 4] = [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4], []]. So if we take n we will get the right inputs for zipping together to produce a window of length n. We can't use zip though, because that needs two lists. So foldr (zipWith (:)) (repeat []) is generalising zip to n inputs and producing a list of lists as outputs (rather than a list of tuples)...

  • Here's my haskell part 2. I noticed you had a comment where you had an issue using foldr, but things work with foldl. The reason for this is that foldr is for a right-associative operator. But the operator as defined in the problem (for part 2) is left associative (if you think about this, it's because you're starting at the beginning of the list and updating the depth based on the current aim). So you can either do foldr op ... (reverse instructions) or foldl op ... instructions. If you're then doing foldl, it's better to use foldl' (from Data.Foldable) as you're more efficient since you collapse as you go, rather than consing all the computation at once. Since foldl doesn't work on infinite lists anyway, this is always safe to do.

    module Main where
    import Data.Foldable (foldl')
    
    data Move = F Int | D Int
      deriving (Eq, Show)
    
    parse :: String -> Maybe Move
    parse ('f':xs) = Just . F $ read (drop 7 xs)
    parse ('d':xs) = Just . D $ read (drop 4 xs)
    parse ('u':xs) = Just . D $ -read (drop 2 xs)
    parse _ = Nothing
    
    inp :: String -> IO (Maybe [Move])
    inp path = do
      c <- readFile path
      return $ mapM parse (lines c)
    
    part1 :: Maybe [Move] -> Int
    part1 Nothing = 0
    part1 (Just xs) = h * d
      where (h, d) = foldl' (\(h, d) x ->
                               case x of
                                 F n -> (h + n, d)
                                 D n -> (h, d + n))
                     (0, 0) xs
    
    part2 :: Maybe [Move] -> Int
    part2 Nothing = 0
    part2 (Just xs) = h * d
      where (h, d, _) = foldl' (\(h, d, a) x ->
                                  case x of
                                    F n -> (h + n, d + (a * n), a)
                                    D n -> (h, d, a + n))
                        (0, 0, 0) xs
    
    main :: IO ()
    main = do
      instructions <- inp "../inputs/2021/day02.input"
      print $ part1 instructions
      print $ part2 instructions
    
About

Avatar for zooeyzooey @zooeyzooey started