Reputation: 434
so, here's a type definition, just for some context:
type Name = String
type Coordinates = (Int, Int)
type Pop = Int
type TotalPop = [Pop]
type City = (Name, (Coordinates, TotalPop))
And here's a data set:
testData :: [City]
testData = [("New York City", ((1,1), [5, 4, 3, 2])),
("Washingotn DC", ((3,3), [3, 2, 1, 1])),
("Los Angeles", ((2,2), [7, 7, 7, 5]))]
So, I'm trying to make a function (addAllPops
) to edit TotalPop
of all City
's in [City]
with a new entry at the start of TotalPop
. I want it to work in such a way that in the example below, the input addNewPop testData [6, 4, 8]
would change them to:
"New York City", ((1,1), [6, 5, 4, 3, 2],
"Washingotn DC", ((3,3), [4, 3, 2, 1, 1],
"Los Angeles", ((2,2), [8, 7, 7, 7, 5]
The function to change just one single city's population is here, along with my attempt for the whole, my biggest issue is entirely around collapsing the two lists into one.
addAllPops :: [City] -> [Int] -> [City]
addAllPops [(w, ((x,y), z))] pops = [map uncurry addPop (zip pops z)]
addPop :: City -> Int -> City
addPop (w, ((x,y), z)) p = (w, ((x,y), p:z))
I've been stuck on this for an embarassingly long amount of time, any and all assistance is greatly appreciated :)
Upvotes: 3
Views: 220
Reputation: 60463
Your instinct to start with addPop
working one at a time is correct. Now look at the type signature of zipWith
:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
It takes a function which operates pointwise and lifts it to operate on two lists in parallel. So you zipWith
the cities and the list of new elements, using addPop
to combine them pointwise:
addAllPops :: [City] -> [Int] -> [City]
addAllPops cities newPops = zipWith addPop cities newPops
We can eta-contract this definition to arrive at the surprisingly simple
addAllPops = zipWith addPop
You could have done this with zip
and map
also, but it's just more plumbing.
Upvotes: 2