我正在尝试使用 zip 和列表理解来编写 zipWith 函数。应用该函数后,我需要压缩两个列表。但是我不知道在哪里使用列表理解。
我尝试对每个列表进行两个列表理解。
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]
我希望该功能与 zipWith 相同,但它没有加载并给出错误:
Occurs check: cannot construct the infinite type:
c ~ (b -> c, b -> c)
Expected type: [c]
Actual type: [(b -> c, b -> c)]
• In the expression: zip [f x | x <- xs] [f y | y <- ys]
In an equation for ‘zipWith'’:
zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]
• Relevant bindings include
ys :: [b] (bound at tutorial5.hs:166:15)
f :: a -> b -> c (bound at tutorial5.hs:166:10)
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
(bound at tutorial5.hs:166:1)
|
166 | zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]
最佳答案
嗯,这里有一些问题。在顶层,考虑 zip :: [a] -> [b] -> [(a,b)]
的签名:它不可能返回 [c]
哪里c
不是元组,所以你不需要 zip
成为新 zipWith
中的外部函数调用。您的类型错误是由于 GHC 注意到它需要强制 c
引起的。是一个元素类型包含 c
的元组它们本身(因为 f
应用于任何东西将始终具有类型 b -> c
)。
您的列表推导式也基本上与 map f xs
相同。和map f ys
。其中第二个无法进行类型检查,因为 ys
的每个元素是 b
,并且您无法申请f
到 b
(它的第一个参数是 a
)。
您可以首先压缩输入列表以获取 [(a,b)]
,然后使用列表理解来运行 f
每对:
zipWith' f xs ys = [f x y | (x,y) <- zip xs ys]
或者,与 map
和uncurry
而不是列表理解:
zipWith' f xs ys = map (uncurry f) $ zip xs ys
或者,使用 GHC 并行列表理解扩展 ( -XParallelListComp
),明确设计为模仿 zip
:
zipWith' f xs ys = [f x y | x <- xs | y <- ys]
如上所述,你不能真正做到 zip
最后,因为它会生成元组。你可以做类似的事情
zipWith' f xs ys = [fx y | (fx, y) <- zip [f x | x <- xs] ys]
适用 f
到第一个列表的元素(在 [f x | x <- xs]
或 map f xs
中),用第二个参数列表压缩此部分应用函数列表,然后将部分函数应用于外部理解中相应的第二个参数,但这有点迂回。
关于haskell - 如何使用 zip 和列表理解来定义 zipWith,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58422267/