3

我正在尝试使用 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]
4

1 回答 1

3

好吧,这里有几个问题。在顶层,考虑 的签名zip :: [a] -> [b] -> [(a,b)]:它不可能返回一个[c]wherec不是元组,所以你不想zip成为 new 中的外部函数调用zipWith。您的类型错误是由于 GHC 注意到它需要强制c成为具有类型包含c自身的元素的事物的元组(因为f应用于任何事物将始终具有 type b -> c)。

map f xs您的列表推导也与and基本相同map f ys。其中第二个无法进行类型检查,因为 的每个元素ys都是 a b,并且您不能应用于fa b(它的第一个参数是 an a)。

相反,您可以先将输入列表压缩为 get [(a,b)],然后使用列表推导f在每一对上运行:

zipWith' f xs ys = [f x y | (x,y) <- zip xs ys]

或者,使用mapuncurry代替列表推导:

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 last,因为它会产生元组。你可以做类似的事情

zipWith' f xs ys = [fx y | (fx, y) <- zip [f x | x <- xs] ys]

它适用f于第一个列表的元素(在[f x | x <- xs]或中map f xs),将这个部分应用函数列表与第二个参数列表压缩,然后将部分函数应用于外部理解中对应的第二个参数,但这有点一种迂回的方式来做到这一点。

于 2019-10-16T22:20:16.133 回答