How do I best map over all elements of a list, except for the last list element?
Say we have a list let l = [1,2,3,4]
and want to get [2,3,4,4]
.
I do have a solution, but it doesn't feel like the "functional" way to do it (in ghci):
let l = [1,2,3,4]
let len = toIntegral $ length l -- to avoid a type mismatch Integer <-> Int
let l1 = zip l [1..]
let l2 = map (\(x,y) -> if y < len then (x + 1,y) else (x,y)) l1
let l3 = map (fst) l2
Not very nice...I do hope there is a better way! Since I'm a novice in functional programming, I don't know where to start looking for it though.
Just re-write map
but make a special case when there's only one element:
mapBut1 :: (a -> a) -> [a] -> [a]
mapBut1 f [] = []
mapBut1 f [x] = [x]
mapBut1 f (x:xs) = f x : mapBut1 f xs
This will now work even for infinite lists, it's a lot faster than calculating the length, and makes it more readable. Note that this does restrict your function to be of type a -> a
instead of a -> b
.
Alternatively, you could do
mapBut1 f (x:y:xs) = f x : mapBut1 f (y:xs)
mapBut1 f other = other
They're equivalent definitions, but the latter uses 1 fewer pattern matches. I would prefer the former, though, since it's more immediately obvious what cases are being handled.
This is a job for pretend-paramorphism, as usual:
import Data.List (tails)
mapButLast :: (a -> a) -> [a] -> [a]
mapButLast f = foldr g [] . tails where
g (x:_:_) r = f x : r
g xs _ = xs
Or with proper para
we'd just write
mapButLast f = para g [] where
g x [] r = [x]
g x _ r = f x : r
where
para f z (x:xs) = f x xs (para f z xs)
para f z [] = z
You can also use init
and last
from standard librairies (no special import) if you don't care about performances.
map f (init list) ++ [last list]
f
: function you want to maplist
: list you want to map overWhat makes this last element so special?
I know it does not answer your question, but I'd consider a type like
data Foo a = Foo a [a]
and an appropriate Functor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With