2

我有点卡住如何重写以下严格评估的列表理解以使用seq而不是 bang 模式:

zipWith' f l1 l2 = [ f e1 e2 | (!e1, !e2) <- zip l1 l2 ]

任何想法 ?

我试过了

zipWith' f l1 l2 = [ e1 `seq` e2 `seq` f e1 e2 | (e1, e2) <- zip l1 l2 ]

但不幸的是,这并不强制对 WHNF 进行评估。

4

3 回答 3

6

您可以将 bang 模式机械地转换为seq 遵循GHC 手册的调用:

这个:

zipWith' f l1 l2 = [ f e1 e2 | (!e1, !e2) <- zip l1 l2 ]

变得太懒了:

zipWith' f l1 l2 =
    [ f e1 e2
    | e <- zip l1 l2
    , let t = case e of (x,y) -> x `seq` y `seq` (x,y)
    , let e1 = fst t
    , let e2 = snd t
    ]

哪个更简洁地写成(太懒了):

zipWith' f l1 l2 =
    [ f e1 e2
    | e <- zip l1 l2
    , let (e1,e2) = case e of (x,y) -> x `seq` y `seq` (x,y)
    ]

虽然我会把它写成(错误的,太懒了)

zipWith' f l1 l2 = zipWith (\x y -> uncurry f (k x y)) l1 l2
    where
        k x y = x `seq` y `seq` (x, y)

您还可以将严格性提示移至数据结构:

data P = P !Integer !Integer

zipWith' f l1 l2 = [ f e1 e2 | P e1 e2 <- zipWith P l1 l2 ]

或者作为:

zipWith' f l1 l2 = [ f e1 e2 | (e1, e2) <- zipWith k l1 l2 ]
    where
        k x y = x `seq` y `seq` (x,y)
于 2011-06-26T19:46:51.450 回答
5

您希望 strictzipWith做的最重要的事情是在强制使用 cons 单元格时评估列表的元素。您的功能不会那样做。试试看嘛

main = print $ length $ zipWith' undefined [1..10] [1..100]

当您使用 (+) 使其工作时,这是严格分析的侥幸。

你想要的功能是这样的:

zipW f (x:xs) (y:ys) = let z = f x y in seq z (z : zipW f xs ys)
zipW _ _ _ = []

您不能将 cons 单元格的生成与值的生成解耦,因为前者应该强制后者。

于 2011-06-26T22:45:36.497 回答
3

解释原始海报的示例和 Don 的一些注意行为。使用 (>>=) 作为简洁的 concatMap,原始示例转换为:

zip l1 l2 >>= \(!e1, !e2) -> [f e1 e2]

第二个例子是:

zip l1 l2 >>= \(e1, e2) -> [e1 `seq` e2 `seq` f e1 e2]

但脱糖的第一个版本实际上进一步脱糖的是:

zip l1 l2 >>= \(e1, e2) -> e1 `seq` e2 `seq` [f e1 e2]

当 concatMap 中的 concat 将这些单例列表变平时, e1 和 e2 会被强制执行,这提供了“我前进时的力量”行为。

虽然这不是人们通常认为的“严格” zipWith,但它适用于 fibs 示例并不是侥幸。

于 2011-06-26T23:23:17.657 回答