我有点卡住如何重写以下严格评估的列表理解以使用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 进行评估。
我有点卡住如何重写以下严格评估的列表理解以使用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 进行评估。
您可以将 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)
您希望 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 单元格的生成与值的生成解耦,因为前者应该强制后者。
解释原始海报的示例和 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 示例并不是侥幸。