如何在Haskell中实现zipWith N

问题描述 投票:0回答:3

在我的代码中,

zipWith7
运行良好,但现在
zipWith7
中的函数需要额外的参数。

zipWith8 (\a b c d e f g h-> ....) List-A  ... List-H

而haskell基础库仅支持最多7个版本。

有没有办法可以将功能拓展到N个喜欢的

zipWithN

haskell
3个回答
3
投票

实现此目的的一种简单方法(并不是说

zipWith8
和朋友真的很常见!)是使用 ZipList 类型和
Applicative
运算符。

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

我相信如果你愿意的话,你可以从中弄清楚写

zipWith9
等。

旁白:这在《Learn You a Haskell》处理应用程序的章节中提到过

除了

zipWith
之外,标准库还具有
zipWith3
zipWith4
等函数,一直到 7 个。
zipWith
接受一个带有两个参数的函数,并用它压缩两个列表。
zipWith3
采用一个带有三个参数的函数并用它压缩三个列表,依此类推。通过使用具有应用风格的 zip 列表,我们不必为要压缩在一起的每个列表提供单独的 zip 函数。我们只是使用应用风格将任意数量的列表与函数压缩在一起,这非常酷。


2
投票

您可以

zip
前两个列表将数量减一,如果您还
uncurry
该函数:

zipWithNplus1 f xs1 xs2 xs3 ... xsNplus1 =
   zipWithN (uncurry f) (zip xs1 xs2) xs3 ... xsNplus1

否则,启用

ParallelListComp
并编写任意数量的并行列表理解:

[ f x1 ... xN
| x1 <- xs1
| x2 <- xs2
| x3 <- xs3
...
| xN <- xsN
]

0
投票

我一直在努力实现同样的事情。这是我想出的第一个解决方案,似乎有效。

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)

实际上,它将列表的长度截断为长度相等,然后转置它们,并将 f 应用于它们。

zipWithN product [[1,2],[2,4],[3,6,3490]]
那么
[6, 48]

我认为必须有一种比使用

null
head
tail
(这些部分函数)更好的方法。基于 this other stackoverflow post,我想出了以下内容:

zipWithN :: ([a] -> b) -> [[a]] -> [b]
zipWithN f xs = map f (getZipList (sequenceA (map ZipList xs)))

或者,用更多的中间变量名来写:

zipWithN :: ([a] -> b) -> [[a]] -> [b]
zipWithN f xs =
  let zipLists = map ZipList xs
      truncatedTransposedZLs = sequenceA zipLists
  in map f (getZipList truncatedTransposedZLs)

请注意,

ZipList
位于
Control.Applicative
中。

© www.soinside.com 2019 - 2024. All rights reserved.