Haskell中的列表理解vs zipWith

时间:2014-05-31 11:22:48

标签: haskell

这是代码#1:

fibs = 0:1:zipWith (+) fibs (tail fibs)

我用list comprehension(代码#2)编写了相同的代码:

fibs' = 0:1:[x+y|x<-fibs',y<-tail fibs']

但代码#1产生Fibonnacci数字,而代码#2产生0 1 1 1 1

为什么会这样?

5 个答案:

答案 0 :(得分:8)

[x+y|x<-fibs',y<-tail fibs']等列表推导会为从这两个列表中提取的x+y的所有组合生成x,y。例如,

[ (x,y) | x<-[1..10] , y<-[1..10] ]

将生成所有100对,基本上计算两个列表的笛卡尔积。 压缩列表只会为相应的元素生成对,只产生10对。

Parallel list comprehensions改为zip。例如,

[ (x,y) | x<-[1..10] | y<-[1..10] ]

将返回与zip相同的10对。您可以通过在文件开头添加{-# LANGUAGE ParallelListComp #-}来启用此Haskell扩展。

就个人而言,我不会使用此扩展程序,而是更愿意明确使用zip

答案 1 :(得分:2)

多个列表的列表推导不像zip / zipWith那样工作 - 一个列表的每个元素与另一个列表的每个元素组合,而不是成对组合。为了说明这种差异,请看一下这个更简单的例子:

xs = [1,2]
ys = [3, 4]
zipped = zipWith (+) xs ys -- [4, 6]
comprehended = [x+y | x <- xs, y <- ys] [4, 5, 5, 6]

要在列表推导中获得zip的行为,您需要使用GHC扩展来进行并行列表推导,这可以让您写下:

parallelComp = [x+y | x <- xs | y <- ys] -- [4, 6]

答案 2 :(得分:2)

原因是:它不是相同的代码。 :)第一个示例使用zipWith成对应用(+)。第二个类似于Cartesian product,但不会返回(x,y)对,而是返回x+y

比较

zip [1..5] [2..6] === [(1,2),(2,3),(3,4),(4,5),(5,6)]

使用:

[ (x,y) | x <- [1..5], y <- [2..6] ] === [(1,2),(1,3),(1,4),(1,5),(1,6),
                                          (2,2),(2,3),(2,4),(2,5),(2,6),
                                          (3,2),(3,3),(3,4),(3,5),(3,6),
                                          (4,2),(4,3),(4,4),(4,5),(4,6),
                                          (5,2),(5,3),(5,4),(5,5),(5,6)]

答案 3 :(得分:1)

列表理解

[ x + y | x <- xs, y <- ys ]

等于(或多或少)以下命令式伪代码

list = emptyList
foreach (x in xs) {
    foreach (y in ys) {
        append (x+y) to list
    }
}
return list

但是,如果ys是一个不定式列表,就像在代码#2中一样,那么结果list将是

list = emptyList
x = head of xs
foreach (y in ys) {
    append (x+y) to list
}
return list

这就是为什么你得到的名单包括0,1,1,......

答案 4 :(得分:1)

您可以使用ZipList而非[]获得所需的行为。由于ZipList不是monad,因此您无法使用monadic do。相反,你必须使用applicative do,也称为“箭头符号”! :)

{-# LANGUAGE Arrows #-}

import Prelude hiding (id, (.))
import Control.Arrow
import Control.Applicative
import Control.Category

data A f a b = A (f (a -> b))
type Arr f a = A f () a

runA :: A f a b -> f (a -> b)
runA (A f) = f

arrOfApp :: Functor f => f a -> Arr f a
arrOfApp = A . fmap const

appOfArr :: Functor f => Arr f a -> f a
appOfArr = fmap ($ ()) . runA

上面的定义与optparse-applicative中的定义非常相似。

zipListArr :: [a] -> Arr ZipList a
zipListArr = arrOfApp . ZipList

getZipListArr :: Arr ZipList a -> [a]
getZipListArr = getZipList . appOfArr

instance Applicative f => Category (A f) where
  id = A (pure id)
  A f . A g = A ((.) <$> f <*> g)

instance Applicative f => Arrow (A f) where
  arr f = A (pure f)
  first (A f) = A (fmap first f)

fibs' :: [Int]
fibs' = 0 : 1 : (getZipListArr $ proc () -> do
  x <- zipListArr fibs' -< ()
  y <- zipListArr (tail fibs') -< ()
  returnA -< x + y)

*Main> take 10 fibs'
[0,1,1,2,3,5,8,13,21,34]