I am wanting to write a function to modify certain elements in a list only if they are a certain value.
For example, for every number that equals 10, I want to add the next two numbers in the list to that original number. So if I have: [10, 9, 1, 8, 2, 10, 10, 6, 4, 5, 4, 7, 3, 3, 4, 3, 7, 1]
then I want a function that will return: [20, 9, 1, 8, 2, 26, 20, 6, 4, 5, 4, 7, 3, 3, 4, 3, 7, 1]
I have explored using maps to do this, but I don't see how I can get a map to use the next two elements in the list to add them to the element that I want to modify.
This is my code so far. I know its obviously flawed by reusing the unchanging values of x:y:z
bowling :: [Int] -> [Int]
bowling (x:y:z:xs) = map (\x -> if (x == 10) then x y z else x) xs
b1 = bowling [10, 9, 1, 8, 2, 10, 10, 6, 4, 5, 4, 7, 3, 3, 4, 3, 7, 1]
I tried using maps to solve the problem, but I don't know how to create a function that will use the next two elements in the list.
CodePudding user response:
This is indeed impossible with just map
. You can do it with explicit recursion, though, like this:
bowling :: [Int] -> [Int]
bowling [] = []
bowling (x:xs) = case x of
10 -> case xs of
y:z:_ -> x y z:bowling xs
_ -> error "Uh oh, got a 10 but one or both of the next two numbers to add were missing"
_ -> x:bowling xs
If you really want to do this with higher-order functions, you could use mapAccumR
or para
to do so instead. For example, with mapAccumR
:
import Data.Traversable (mapAccumR)
bowling :: [Int] -> [Int]
bowling = snd . mapAccumR go (uhoh,uhoh) where
go :: (Int,Int) -> Int -> ((Int,Int), Int)
go ~(y,z) x = ((x,y), if x == 10 then x y z else x)
uhoh :: Int
uhoh = error "Uh oh, got a 10 but one or both of the next two numbers to add were missing"