Since you only use call the nonpartial case of tlTrns
and only use it once, you can inline it. Pattern matching is better than head
/tail
/fst
/snd
. Maybe
's fmap
can be used again.
-- | Given a 2D list, select a list via index,
-- transfer the last item of its immediate non blank
-- left neighbour to its head.
pull :: Int -- based 1 index
-> [[a]] -- 2D list
-> Maybe [[a]] -- result after operation
pull _ [] = Nothing -- empty list / out of bounds
pull 1 _ = Nothing -- no left neighbour
pull 2 ([]:_) = Nothing -- empty left neighbour
pull 2 (xs:ys:yss) = Just $ let (pl, pr) = splLst xs
in pl:(pr:ys):yss -- base case take from left neighbour
pull n (xs:[]:xss) = fmap (\(p:ps) -> p:[]:ps) (pull (n-1) (xs:xss))
-- carry over due to blank mid
pull n (xs:xss) = fmap (xs:) (pull (n-1) xss) -- recurse tail
You can make splLst
nonpartial by using Maybe
. Then it turns out that Data.List.Extra
already implements that and calls it unsnoc
. Using it, you can eliminate another case from pull.
import Data.List.Extra (unsnoc)
-- | Given a 2D list, select a list via index,
-- transfer the last item of its immediate non blank
-- left neighbour to its head.
pull :: Int -- based 1 index
-> [[a]] -- 2D list
-> Maybe [[a]] -- result after operation
pull _ [] = Nothing -- empty list / out of bounds
pull 1 _ = Nothing -- no left neighbour
pull 2 (xs:ys:yss) = fmap (\(pl,pr) -> pl:(pr:ys):yss) (unsnoc xs)
-- base case take from left neighbour
pull n (xs:[]:xss) = fmap (\(p:ps) -> p:[]:ps) (pull (n-1) (xs:xss))
-- carry over due to blank mid
pull n (xs:xss) = fmap (xs:) (pull (n-1) xss) -- recurse tail