Reputation: 1884
In my code, zipWith7
works well but now the function in zipWith7
require extra arity .
zipWith8 (\a b c d e f g h-> ....) List-A ... List-H
while haskell base library only support up to 7 arity version.
Is there a way that can expand the function to N likr zipWithN
?
Upvotes: 0
Views: 307
Reputation: 95
I have been trying to implement the same thing. This was the first solution I came up with, which seems to work.
zipWithN :: ([a] -> b) -> [[a]] -> [b]
zipWithN f = go where
go [] = []
go lists | any null lists = []
| otherwise =
let heads = map head lists
in f heads : go (map tail lists)
In effect, it truncates the lengths of the lists to be equal in length, then transposes them, and applies f to them. zipWithN product [[1,2],[2,4],[3,6,3490]]
is then [6, 48]
.
I thought there had to be a better way than to check the entire list for empty lists every single time, and something better than using null
, head
, and tail
, which are partial functions. Based on this other stackoverflow post, I came up with the following:
zipWithN :: ([a] -> b) -> [[a]] -> [b]
zipWithN f = map f . getZipList . sequenceA . map ZipList
Or, written with more intermediate variable names:
zipWithN :: ([a] -> b) -> [[a]] -> [b]
zipWithN f xs =
let zipLists = map ZipList xs
truncatedTransposedZLs = sequenceA zipLists
in map f (getZipList truncatedTransposedZLs)
Note that ZipList
is in Control.Applicative
.
Edit: This can be simplified to
zipWithN :: ([a] -> b) -> [[a]] -> [b]
zipWithN f = map f . getZipList . traverse ZipList
Upvotes: 1
Reputation: 18249
One simple way to do this (not that zipWith8
and friends are really commonly needed!) is to make use of the ZipList type and the Applicative
operators.
zipWith8 f a b c d e f g h = getZipList $
f <$> ZipList a
<*> ZipList b
<*> ZipList c
<*> ZipList d
<*> ZipList e
<*> ZipList f
<*> ZipList g
<*> ZipList h
I'm sure you can figure out from this to write zipWith9
etc if you wanted to.
Aside: this is mentioned in the chapter of Learn You a Haskell dealing with Applicatives:
Aside from
zipWith
, the standard library has functions such aszipWith3
,zipWith4
, all the way up to 7.zipWith
takes a function that takes two parameters and zips two lists with it.zipWith3
takes a function that takes three parameters and zips three lists with it, and so on. By using zip lists with an applicative style, we don't have to have a separate zip function for each number of lists that we want to zip together. We just use the applicative style to zip together an arbitrary amount of lists with a function, and that's pretty cool.
Upvotes: 3
Reputation: 116139
You can zip
the first two lists to reduce the arity by one, if you also uncurry
the function:
zipWithNplus1 f xs1 xs2 xs3 ... xsNplus1 =
zipWithN (uncurry f) (zip xs1 xs2) xs3 ... xsNplus1
Otherwise, enable ParallelListComp
and write a parallel list comprehension of any arity:
[ f x1 ... xN
| x1 <- xs1
| x2 <- xs2
| x3 <- xs3
...
| xN <- xsN
]
Upvotes: 3