这是代码#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
为什么会这样?
答案 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]